major updates

This commit is contained in:
Zacharias-Brohn
2025-11-24 22:00:50 +01:00
parent 5593cce7ca
commit 91a4f95fd0
26 changed files with 1287 additions and 447 deletions
+82 -87
View File
@@ -1,11 +1,15 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import qs.Modules
import qs.Modules.Bar
import qs.Config
import qs.Helpers
import qs.Drawers
Scope {
Variants {
@@ -16,11 +20,12 @@ Scope {
required property var modelData
property bool trayMenuVisible: false
screen: modelData
color: "transparent"
property var root: Quickshell.shellDir
WlrLayershell.exclusionMode: ExclusionMode.Ignore
PanelWindow {
id: wrapper
id: exclusionZone
screen: bar.screen
WlrLayershell.layer: WlrLayer.Bottom
anchors {
@@ -49,99 +54,89 @@ Scope {
bottom: true
}
mask: Region { item: backgroundRect }
mask: Region {
x: 0
y: 34
color: "transparent"
width: bar.width
height: bar.screen.height - backgroundRect.implicitHeight
intersection: Intersection.Xor
Rectangle {
id: backgroundRect
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: 34
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
radius: 0
regions: popoutRegions.instances
}
Behavior on color {
CAnim {}
Variants {
id: popoutRegions
model: panels.children
Region {
required property Item modelData
x: modelData.x
y: modelData.y + backgroundRect.implicitHeight
width: modelData.width
height: modelData.height
intersection: Intersection.Subtract
}
}
Item {
anchors.fill: parent
Backgrounds {
panels: panels
bar: backgroundRect
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: {
if ( !containsMouse ) {
panels.popouts.hasCurrent = false;
}
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 5
anchors.rightMargin: 5
RowLayout {
id: leftSection
Layout.fillHeight: true
Layout.preferredWidth: leftSection.childrenRect.width
Workspaces {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
bar: bar
}
AudioWidget {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
Resources {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
UpdatesWidget {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
countUpdates: Updates.availableUpdates
}
}
RowLayout {
id: centerSection
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
}
RowLayout {
id: rightSection
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
TrayWidget {
id: systemTrayModule
bar: bar
Layout.alignment: Qt.AlignVCenter
}
Clock {
Layout.alignment: Qt.AlignVCenter
}
Text {
id: notificationCenterIcon
property color iconColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "white"
Layout.alignment: Qt.AlignVCenter
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
font.family: "Material Symbols Rounded"
font.pixelSize: 20
color: iconColor
Behavior on color {
CAnim {}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
ncProcess.running = true
}
}
}
onPositionChanged: event => {
if ( mouseY < backgroundRect.implicitHeight ) {
barLoader.checkPopout(mouseX);
}
}
WindowTitle {
anchors.centerIn: parent
width: Math.min( 300, parent.width * 0.4 )
height: parent.height
z: 1
Panels {
id: panels
screen: bar.modelData
bar: backgroundRect
}
Rectangle {
id: backgroundRect
property Wrapper popouts: panels.popouts
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: 34
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
radius: 0
Behavior on color {
CAnim {}
}
BarLoader {
id: barLoader
anchors.fill: parent
popouts: panels.popouts
bar: bar
}
WindowTitle {
anchors.centerIn: parent
width: Math.min( 300, parent.width * 0.4 )
height: parent.height
z: 1
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
import Quickshell.Io
JsonObject {
property Popouts popouts: Popouts {}
property list<var> entries: [
{
id: "workspaces",
enabled: true
},
{
id: "audio",
enabled: true
},
{
id: "resources",
enabled: true
},
{
id: "updates",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "activeWindow",
enabled: true
},
{
id: "spacer",
enabled: true
},
{
id: "tray",
enabled: true
},
{
id: "clock",
enabled: true
},
{
id: "notifBell",
enabled: true
},
]
component Popouts: JsonObject {
property bool tray: true
property bool audio: true
property bool activeWindow: false
property bool resources: true
}
}
+2
View File
@@ -18,6 +18,7 @@ Singleton {
property alias gpuType: adapter.gpuType
property alias background: adapter.background
property alias useDynamicColors: adapter.useDynamicColors
property alias barConfig: adapter.barConfig
FileView {
id: root
@@ -44,6 +45,7 @@ Singleton {
property string gpuType: ""
property BackgroundConfig background: BackgroundConfig {}
property bool useDynamicColors: false
property BarConfig barConfig: BarConfig {}
}
}
}
+22
View File
@@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Shapes
import qs.Modules as Modules
Shape {
id: root
required property Panels panels
required property Item bar
anchors.fill: parent
anchors.margins: 8
anchors.topMargin: bar.implicitHeight
preferredRendererType: Shape.CurveRenderer
Modules.Background {
wrapper: root.panels.popouts
startX: Math.floor(wrapper.x - rounding)
startY: wrapper.y
}
}
+33
View File
@@ -0,0 +1,33 @@
import Quickshell
import QtQuick
import QtQuick.Shapes
import qs.Modules as Modules
import qs.Config
Item {
id: root
required property ShellScreen screen
required property Item bar
readonly property alias popouts: popouts
anchors.fill: parent
anchors.margins: 8
anchors.topMargin: bar.implicitHeight
Modules.Wrapper {
id: popouts
screen: root.screen
anchors.top: parent.top
x: {
const off = currentCenter - 8 - nonAnimWidth / 2;
const diff = root.width - Math.floor(off + nonAnimWidth);
if (diff < 0)
return off + diff;
return Math.max(off, 0);
}
}
}
+31 -35
View File
@@ -9,34 +9,13 @@ import qs.Config
import qs.Components
import qs.Daemons
CustomRect {
Item {
id: root
implicitWidth: layout.implicitWidth + 10 * 2
implicitHeight: 0
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : "#40000000"
clip: true
implicitHeight: layout.implicitHeight + 10 * 2
property alias expanded: root.isExpanded
property bool isExpanded: false
Anim {
id: expandAnim
running: root.isExpanded
target: root
property: "implicitHeight"
to: layout.implicitHeight + 10 * 2
duration: MaterialEasing.standardTime
}
Anim {
id: collapseAnim
running: !root.isExpanded
target: root
property: "implicitHeight"
to: 0
duration: MaterialEasing.standardTime
}
required property var wrapper
ButtonGroup {
id: sinks
@@ -49,8 +28,8 @@ CustomRect {
ColumnLayout {
id: layout
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
spacing: 12
CustomText {
@@ -95,7 +74,7 @@ CustomRect {
CustomText {
Layout.topMargin: 10
Layout.bottomMargin: -7 / 2
text: qsTr("Volume (%1)").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
text: qsTr("Output Volume (%1)").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
font.weight: 500
}
@@ -103,13 +82,6 @@ CustomRect {
Layout.fillWidth: true
implicitHeight: 10 * 3
onWheel: event => {
if (event.angleDelta.y > 0)
Audio.incrementVolume();
else if (event.angleDelta.y < 0)
Audio.decrementVolume();
}
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
@@ -124,6 +96,31 @@ CustomRect {
}
}
CustomText {
Layout.topMargin: 10
Layout.bottomMargin: -7 / 2
text: qsTr("Input Volume (%1)").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`)
font.weight: 500
}
CustomMouseArea {
Layout.fillWidth: true
implicitHeight: 10 * 3
CustomSlider {
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: parent.implicitHeight
value: Audio.sourceVolume
onMoved: Audio.setSourceVolume(value)
Behavior on value {
Anim {}
}
}
}
CustomRect {
Layout.topMargin: 12
visible: true
@@ -138,7 +135,6 @@ CustomRect {
color: DynamicColors.palette.m3onPrimaryContainer
function onClicked(): void {
root.isExpanded = !root.isExpanded;
Quickshell.execDetached(["app2unit", "--", "pavucontrol"]);
}
}
-8
View File
@@ -53,19 +53,11 @@ Item {
border.width: 0
}
AudioPopup {
id: audioPopup
anchors.left: parent.left
anchors.top: parent.bottom
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: audioPopup.expanded = true
onExited: audioPopup.expanded = false
RowLayout {
anchors {
+44 -59
View File
@@ -1,79 +1,64 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Helpers
import QtQuick.Shapes
import qs.Config
Item {
ShapePath {
id: root
property string source: SearchWallpapers.current
property Image current: one
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
anchors.fill: parent
strokeWidth: -1
fillColor: DynamicColors.tPalette.m3surface
onSourceChanged: {
if (!source) {
current = null;
} else if (current === one) {
two.update();
} else {
one.update();
}
PathArc {
relativeX: root.rounding
relativeY: root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
}
Component.onCompleted: {
console.log(root.source)
if (source)
Qt.callLater(() => one.update());
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.roundingY * 2
}
Img {
id: one
PathArc {
relativeX: root.rounding
relativeY: root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: PathArc.Counterclockwise
}
Img {
id: two
PathLine {
relativeX: root.wrapper.width - root.rounding * 2
relativeY: 0
}
component Img: CachingImage {
id: img
PathArc {
relativeX: root.rounding
relativeY: -root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: PathArc.Counterclockwise
}
function update(): void {
if (path === root.source) {
root.current = this;
} else {
path = root.source;
}
}
PathLine {
relativeX: 0
relativeY: -(root.wrapper.height - root.roundingY * 2)
}
anchors.fill: parent
PathArc {
relativeX: root.rounding
relativeY: -root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
}
opacity: 0
scale: SearchWallpapers.showPreview ? 1 : 0.8
asynchronous: true
onStatusChanged: {
if (status === Image.Ready) {
root.current = this;
}
}
states: State {
name: "visible"
when: root.current === img
PropertyChanges {
img.opacity: 1
img.scale: 1
}
}
transitions: Transition {
Anim {
target: img
properties: "opacity,scale"
duration: Config.background.wallFadeDuration
}
}
Behavior on fillColor {
CAnim {}
}
}
+165
View File
@@ -0,0 +1,165 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Modules
import qs.Config
import qs.Helpers
import qs.Daemons
RowLayout {
id: root
anchors.fill: parent
anchors.leftMargin: 5
anchors.rightMargin: 5
readonly property int vPadding: 6
required property Wrapper popouts
required property PanelWindow bar
function checkPopout(x: real): void {
const ch = childAt(x, height / 2) as WrappedLoader;
if (!ch) {
popouts.hasCurrent = false;
return;
}
const id = ch.id;
const top = ch.x;
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.hasCurrent = true;
} else if ( id === "resources" && Config.barConfig.popouts.resources ) {
popouts.currentName = "resources";
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 - 6) / 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 === "activeWindow" && Config.barConfig.popouts.activeWindow) {
popouts.currentName = id.toLowerCase();
popouts.currentCenter = item.mapToItem(root, 0, itemHeight / 2).y;
popouts.hasCurrent = true;
}
}
Repeater {
id: repeater
model: Config.barConfig.entries
DelegateChooser {
role: "id"
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 {}
}
}
DelegateChoice {
roleValue: "tray"
delegate: WrappedLoader {
sourceComponent: TrayWidget {
bar: root.bar
}
}
}
DelegateChoice {
roleValue: "resources"
delegate: WrappedLoader {
sourceComponent: Resources {}
}
}
DelegateChoice {
roleValue: "updates"
delegate: WrappedLoader {
sourceComponent: UpdatesWidget {}
}
}
DelegateChoice {
roleValue: "notifBell"
delegate: WrappedLoader {
sourceComponent: NotifBell {
Layout.alignment: Qt.AlignHCenter
}
}
}
DelegateChoice {
roleValue: "clock"
delegate: WrappedLoader {
sourceComponent: Clock {
Layout.alignment: Qt.AlignHCenter
}
}
}
DelegateChoice {
roleValue: "activeWindow"
delegate: WrappedLoader {
sourceComponent: WindowTitle {}
}
}
}
}
component WrappedLoader: Loader {
required property bool enabled
required property string id
required property int index
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 null;
}
function findLastEnabled(): Item {
for (let i = repeater.count - 1; i >= 0; i--) {
const item = repeater.itemAt(i);
if (item?.enabled)
return item;
}
return null;
}
Layout.alignment: Qt.AlignHCenter
// Cursed ahh thing to add padding to first and last enabled components
Layout.topMargin: findFirstEnabled() === this ? root.vPadding : 0
Layout.bottomMargin: findLastEnabled() === this ? root.vPadding : 0
visible: enabled
active: enabled
}
}
+17
View File
@@ -0,0 +1,17 @@
import QtQuick
import qs.Config
import qs.Modules
Item {
implicitWidth: timeText.contentWidth
implicitHeight: timeText.contentHeight
Text {
id: timeText
text: Time.time
color: Config.useDynamicColors ? DynamicColors.palette.m3tertiary : "white"
Behavior on color {
CAnim {}
}
}
}
+133
View File
@@ -0,0 +1,133 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.SystemTray
import QtQuick
import qs.Config
Item {
id: root
required property Item wrapper
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
readonly property Item current: currentPopout?.item ?? null
anchors.centerIn: parent
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 10 * 2
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 10 * 2
Item {
id: content
anchors.fill: parent
anchors.margins: 10
Popout {
name: "audio"
sourceComponent: AudioPopup {
wrapper: root.wrapper
}
}
Popout {
name: "resources"
sourceComponent: ResourcePopout {
wrapper: root.wrapper
}
}
Repeater {
model: ScriptModel {
values: [ ...SystemTray.items.values ]
}
Popout {
id: trayMenu
required property SystemTrayItem modelData
required property int index
name: `traymenu${index}`
sourceComponent: trayMenuComponent
Connections {
target: root.wrapper
function onHasCurrentChanged(): void {
if ( root.wrapper.hasCurrent && trayMenu.shouldBeActive ) {
trayMenu.sourceComponent = null;
trayMenu.sourceComponent = trayMenuComponent;
}
}
}
Component {
id: trayMenuComponent
TrayMenuPopout {
popouts: root.wrapper
trayItem: trayMenu.modelData.menu
}
}
}
}
}
component Popout: Loader {
id: popout
required property string name
readonly property bool shouldBeActive: root.wrapper.currentName === name
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
opacity: 0
scale: 0.8
active: false
states: State {
name: "active"
when: popout.shouldBeActive
PropertyChanges {
popout.active: true
popout.opacity: 1
popout.scale: 1
}
}
transitions: [
Transition {
from: "active"
to: ""
SequentialAnimation {
Anim {
properties: "opacity,scale"
duration: MaterialEasing.expressiveEffectsTime
}
PropertyAction {
target: popout
property: "active"
}
}
},
Transition {
from: ""
to: "active"
SequentialAnimation {
PropertyAction {
target: popout
property: "active"
}
Anim {
properties: "opacity,scale"
}
}
}
]
}
}
-2
View File
@@ -4,8 +4,6 @@ import Quickshell
Singleton {
id: root
// thanks to Soramane :>
// expressive curves => thanks end cutie ;)
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property int emphasizedAccelTime: 200
+28
View File
@@ -0,0 +1,28 @@
import QtQuick
import qs.Config
import qs.Helpers
Item {
implicitWidth: 20
implicitHeight: 18
Text {
id: notificationCenterIcon
property color iconColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "white"
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
font.family: "Material Symbols Rounded"
font.pixelSize: 20
color: iconColor
Behavior on color {
CAnim {}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
ncProcess.running = true
}
}
}
}
+2 -3
View File
@@ -14,9 +14,8 @@ Item {
property color warningBarColor: Config.useDynamicColors ? DynamicColors.palette.m3error : Config.accentColor.accents.warning
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "#ffffff"
height: columnLayout.childrenRect.height
anchors.left: parent.left
anchors.right: parent.right
Layout.preferredWidth: 158
Layout.preferredHeight: columnLayout.implicitHeight
ColumnLayout {
id: columnLayout
+61
View File
@@ -0,0 +1,61 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Config
Item {
id: popoutWindow
implicitWidth: contentColumn.implicitWidth + 10 * 2
implicitHeight: contentColumn.implicitHeight + 10
required property var wrapper
// ShadowRect {
// anchors.fill: contentRect
// radius: 8
// }
ColumnLayout {
id: contentColumn
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
spacing: 10
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 ))
}
ResourceDetail {
resourceName: qsTr( "CPU Usage" )
iconString: "\ue322"
percentage: ResourceUsage.cpuUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.cpuUsage * 100 ))
}
ResourceDetail {
resourceName: qsTr( "GPU Usage" )
iconString: "\ue30f"
percentage: ResourceUsage.gpuUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.gpuUsage * 100 ))
}
ResourceDetail {
resourceName: qsTr( "VRAM Usage" )
iconString: "\ue30d"
percentage: ResourceUsage.gpuMemUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.gpuMemUsage * 100 ))
}
}
}
-107
View File
@@ -94,111 +94,4 @@ Item {
}
}
}
Item {
id: popoutWindow
z: 0
property int rectHeight: contentRect.implicitHeight
anchors.fill: parent
visible: true
// ShadowRect {
// anchors.fill: contentRect
// radius: 8
// }
ParallelAnimation {
id: openAnim
Anim {
target: contentRect
property: "implicitHeight"
to: contentColumn.childrenRect.height + 20
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
ParallelAnimation {
id: closeAnim
Anim {
target: contentRect
property: "implicitHeight"
to: 0
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Rectangle {
id: contentRect
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.bottom
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
border.color: Config.useDynamicColors ? "transparent" : Config.baseBorderColor
border.width: 1
bottomLeftRadius: 8
bottomRightRadius: 8
clip: true
Column {
id: contentColumn
anchors.fill: parent
anchors.margins: 10
spacing: 10
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 ))
}
ResourceDetail {
resourceName: qsTr( "CPU Usage" )
iconString: "\ue322"
percentage: ResourceUsage.cpuUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.cpuUsage * 100 ))
}
ResourceDetail {
resourceName: qsTr( "GPU Usage" )
iconString: "\ue30f"
percentage: ResourceUsage.gpuUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.gpuUsage * 100 ))
}
ResourceDetail {
resourceName: qsTr( "VRAM Usage" )
iconString: "\ue30d"
percentage: ResourceUsage.gpuMemUsage
warningThreshold: 95
details: qsTr( "%1% used" )
.arg( Math.round( ResourceUsage.gpuMemUsage * 100 ))
}
}
}
MouseArea {
id: widgetMouseArea
z: 1
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: contentRect.bottom
hoverEnabled: true
onEntered: {
openAnim.start();
}
onExited: {
closeAnim.start();
}
}
}
}
View File
+1 -44
View File
@@ -8,39 +8,13 @@ import qs.Config
import Caelestia
import QtQuick.Effects
MouseArea {
Item {
id: root
required property SystemTrayItem item
required property PanelWindow bar
property point globalPos
property bool hasLoaded: false
implicitWidth: 24
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPositionChanged: {
globalPos = root.mapToItem(root.bar.backgroundRect, 0, 0);
}
Rectangle {
anchors.centerIn: parent
implicitHeight: 28
implicitWidth: 28
radius: 6
anchors.verticalCenter: parent.verticalCenter
color: root.containsMouse ? Config.colors.backgrounds.hover : "transparent"
Behavior on color {
ColorAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
Image {
id: icon
@@ -58,22 +32,5 @@ MouseArea {
sourceSize.height: ( batteryHDPI || nmHDPI ) ? 16 : 22
fillMode: Image.PreserveAspectFit
TrayMenu {
id: trayMenu
trayMenu: root.item?.menu
trayItemRect: root.globalPos
bar: root.bar
}
}
onClicked: {
if ( mouse.button === Qt.LeftButton ) {
root.item.activate();
} else if ( mouse.button === Qt.RightButton ) {
trayMenu.trayMenu = null;
trayMenu.trayMenu = root.item?.menu;
trayMenu.visible = !trayMenu.visible;
trayMenu.focusGrab = true;
}
}
}
+19 -4
View File
@@ -8,6 +8,8 @@ import Qt5Compat.GraphicalEffects
import Quickshell.Hyprland
import QtQml
import qs.Effects
import qs.Config
import qs.Modules
PanelWindow {
id: root
@@ -24,6 +26,12 @@ PanelWindow {
property int biggestWidth: 0
property int menuItemCount: menuOpener.children.values.length
property color backgroundColor: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor
property color highlightColor: Config.useDynamicColors ? DynamicColors.tPalette.m3primaryContainer : "#15FFFFFF"
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "white"
property color disabledHighlightColor: Config.useDynamicColors ? DynamicColors.layer(DynamicColors.palette.m3primaryContainer, 0) : "#08FFFFFF"
property color disabledTextColor: Config.useDynamicColors ? DynamicColors.layer(DynamicColors.palette.m3onSurface, 0) : "#80FFFFFF"
QsMenuOpener {
id: menuOpener
menu: root.trayMenu
@@ -183,8 +191,9 @@ PanelWindow {
y: Math.round( root.trayItemRect.y - 5 )
implicitWidth: listLayout.contentWidth + 10
implicitHeight: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
color: "#80151515"
color: root.backgroundColor
radius: 8
border.width: Config.useDynamicColors ? 0 : 1
border.color: "#40FFFFFF"
clip: true
@@ -218,7 +227,7 @@ PanelWindow {
id: listLayout
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
spacing: 2
spacing: 0
contentWidth: root.biggestWidth
contentHeight: contentItem.childrenRect.height
model: menuOpener.children
@@ -236,10 +245,16 @@ PanelWindow {
anchors.left: parent.left
anchors.right: parent.right
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
color: menuItem.modelData.isSeparator ? "#20FFFFFF" : containsMouseAndEnabled ? "#15FFFFFF" : containsMouseAndNotEnabled ? "#08FFFFFF" : "transparent"
color: menuItem.modelData.isSeparator ? "#20FFFFFF" : containsMouseAndEnabled ? root.highlightColor : containsMouseAndNotEnabled ? root.disabledHighlightColor : "transparent"
radius: 4
visible: true
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;
@@ -284,7 +299,7 @@ PanelWindow {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.leftMargin: 10
text: menuItem.modelData.text
color: menuItem.modelData.enabled ? "white" : "gray"
color: menuItem.modelData.enabled ? root.textColor : root.disabledTextColor
}
Image {
id: iconImage
+230
View File
@@ -0,0 +1,230 @@
pragma ComponentBehavior: Bound
import qs.Components
import qs.Config
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Controls
StackView {
id: root
required property Item popouts
required property QsMenuHandle trayItem
property int biggestWidth: 0
implicitWidth: currentItem.implicitWidth
implicitHeight: currentItem.implicitHeight
initialItem: SubMenu {
handle: root.trayItem
}
pushEnter: NoAnim {}
pushExit: NoAnim {}
popEnter: NoAnim {}
popExit: NoAnim {}
component NoAnim: Transition {
NumberAnimation {
duration: 0
}
}
component SubMenu: Column {
id: menu
required property QsMenuHandle handle
property bool isSubMenu
property bool shown
padding: 0
spacing: 4
opacity: shown ? 1 : 0
scale: shown ? 1 : 0.8
Component.onCompleted: shown = true
StackView.onActivating: shown = true
StackView.onDeactivating: shown = false
StackView.onRemoved: destroy()
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
QsMenuOpener {
id: menuOpener
menu: menu.handle
}
Repeater {
model: menuOpener.children
CustomRect {
id: item
required property QsMenuEntry modelData
implicitWidth: root.biggestWidth
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
radius: 4
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
Loader {
id: children
anchors.left: parent.left
anchors.right: parent.right
active: !item.modelData.isSeparator
asynchronous: true
sourceComponent: Item {
implicitHeight: 30
StateLayer {
radius: item.radius
disabled: !item.modelData.enabled
function onClicked(): void {
const entry = item.modelData;
if (entry.hasChildren)
root.push(subMenuComp.createObject(null, {
handle: entry,
isSubMenu: true
}));
else {
item.modelData.triggered();
root.popouts.hasCurrent = false;
}
}
}
Loader {
id: icon
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 10
active: item.modelData.icon !== ""
asynchronous: true
sourceComponent: IconImage {
implicitSize: label.implicitHeight
source: item.modelData.icon
}
}
CustomText {
id: label
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
text: labelMetrics.elidedText
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
}
TextMetrics {
id: labelMetrics
text: item.modelData.text
font.pointSize: label.font.pointSize
font.family: label.font.family
Component.onCompleted: {
var biggestWidth = root.biggestWidth;
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
if ( currentWidth > biggestWidth ) {
root.biggestWidth = currentWidth;
}
}
}
Loader {
id: expand
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
active: item.modelData.hasChildren
asynchronous: true
sourceComponent: MaterialIcon {
text: "chevron_right"
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
}
}
}
}
}
}
Loader {
active: menu.isSubMenu
asynchronous: true
sourceComponent: Item {
implicitWidth: back.implicitWidth
implicitHeight: back.implicitHeight + 2 / 2
Item {
anchors.bottom: parent.bottom
implicitWidth: back.implicitWidth
implicitHeight: back.implicitHeight
CustomRect {
anchors.fill: parent
radius: 1000
color: DynamicColors.palette.m3secondaryContainer
StateLayer {
radius: parent.radius
color: DynamicColors.palette.m3onSecondaryContainer
function onClicked(): void {
root.pop();
}
}
}
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 {}
}
}
+13 -19
View File
@@ -5,27 +5,21 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Services.SystemTray
Rectangle {
Row {
id: root
required property PanelWindow bar
implicitHeight: parent.height
implicitWidth: rowL.implicitWidth + 10
color: "transparent"
RowLayout {
spacing: 5
id: rowL
anchors.centerIn: parent
Repeater {
id: repeater
model: SystemTray.items
TrayItem {
id: trayItem
required property SystemTrayItem modelData
implicitHeight: root.implicitHeight
item: modelData
bar: root.bar
}
readonly property alias items: repeater
spacing: 0
Repeater {
id: repeater
model: SystemTray.items
TrayItem {
id: trayItem
required property SystemTrayItem modelData
implicitHeight: 34
implicitWidth: 28
item: modelData
bar: root.bar
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ import qs.Config
Item {
id: root
required property int countUpdates
property int countUpdates: Updates.availableUpdates
implicitWidth: contentRow.childrenRect.width + 10
implicitHeight: 22
property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3tertiaryFixed : "#ffffff"
+79
View File
@@ -0,0 +1,79 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Helpers
import qs.Config
Item {
id: root
property string source: SearchWallpapers.current
property Image current: one
anchors.fill: parent
onSourceChanged: {
if (!source) {
current = null;
} else if (current === one) {
two.update();
} else {
one.update();
}
}
Component.onCompleted: {
console.log(root.source)
if (source)
Qt.callLater(() => one.update());
}
Img {
id: one
}
Img {
id: two
}
component Img: CachingImage {
id: img
function update(): void {
if (path === root.source) {
root.current = this;
} else {
path = root.source;
}
}
anchors.fill: parent
opacity: 0
scale: SearchWallpapers.showPreview ? 1 : 0.8
asynchronous: true
onStatusChanged: {
if (status === Image.Ready) {
root.current = this;
}
}
states: State {
name: "visible"
when: root.current === img
PropertyChanges {
img.opacity: 1
img.scale: 1
}
}
transitions: Transition {
Anim {
target: img
properties: "opacity,scale"
duration: Config.background.wallFadeDuration
}
}
}
}
+82 -77
View File
@@ -8,106 +8,111 @@ import Quickshell
import Quickshell.Hyprland
import qs.Config
Rectangle {
id: root
Item {
id: itemRoot
required property PanelWindow bar
property HyprlandMonitor monitor: Hyprland.monitorFor( root.bar?.screen )
implicitHeight: 28
implicitWidth: root.implicitWidth
Rectangle {
id: root
implicitWidth: workspacesRow.implicitWidth + 6
implicitHeight: 22
property HyprlandMonitor monitor: Hyprland.monitorFor( itemRoot.bar?.screen )
function shouldShow(monitor) {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
if ( monitor === root.monitor ) {
return true;
} else {
return false;
implicitWidth: workspacesRow.implicitWidth + 6
implicitHeight: 22
function shouldShow(monitor) {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
if ( monitor === root.monitor ) {
return true;
} else {
return false;
}
}
}
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
radius: height / 2
color: Config.useDynamicColors ? DynamicColors.tPalette.m3surfaceContainer : "#40000000"
radius: height / 2
Behavior on implicitWidth {
NumberAnimation {
duration: 100
easing.type: Easing.InOutQuad
Behavior on implicitWidth {
NumberAnimation {
duration: 100
easing.type: Easing.InOutQuad
}
}
}
Behavior on color {
CAnim {}
}
Behavior on color {
CAnim {}
}
RowLayout {
id: workspacesRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 3
spacing: 8
RowLayout {
id: workspacesRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 3
spacing: 8
Repeater {
model: Hyprland.workspaces
Repeater {
model: Hyprland.workspaces
Rectangle {
id: workspaceIndicator
required property var modelData
Rectangle {
id: workspaceIndicator
required property var modelData
width: 16
height: 16
radius: height / 2
width: 16
height: 16
radius: height / 2
color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3primary : Config.accentColor.accents.primary ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#606060" )
color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3primary : Config.accentColor.accents.primary ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#606060" )
border.color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3onPrimary : Config.accentColor.accents.primaryAlt ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#808080" )
border.width: 1
border.color: modelData.id === Hyprland.focusedWorkspace.id ? ( Config.useDynamicColors ? DynamicColors.palette.m3onPrimary : Config.accentColor.accents.primaryAlt ) : ( Config.useDynamicColors ? DynamicColors.palette.m3inverseOnSurface : "#808080" )
border.width: 1
visible: root.shouldShow( modelData.monitor )
visible: root.shouldShow( modelData.monitor )
scale: 1.0
opacity: 1.0
scale: 1.0
opacity: 1.0
Behavior on color {
ColorAnimation {
duration: 150
easing.type: Easing.InOutQuad
Behavior on color {
ColorAnimation {
duration: 150
easing.type: Easing.InOutQuad
}
}
}
Behavior on border.color {
ColorAnimation {
duration: 150
easing.type: Easing.InOutQuad
Behavior on border.color {
ColorAnimation {
duration: 150
easing.type: Easing.InOutQuad
}
}
}
NumberAnimation on scale {
from: 0.0
to: 1.0
duration: 300
easing.type: Easing.OutBack
}
NumberAnimation on opacity {
from: 0.0
to: 1.0
duration: 200
}
NumberAnimation on scale {
from: 0.0
to: 1.0
duration: 300
easing.type: Easing.OutBack
}
NumberAnimation on opacity {
from: 0.0
to: 1.0
duration: 200
}
// Text {
// anchors.centerIn: parent
// text: modelData.id
// font.pixelSize: 10
// font.family: "Rubik"
// color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
// }
// Text {
// anchors.centerIn: parent
// text: modelData.id
// font.pixelSize: 10
// font.family: "Rubik"
// color: modelData.id === Hyprland.focusedWorkspace.id ? Config.workspaceWidget.textColor : Config.workspaceWidget.inactiveTextColor
// }
MouseArea {
anchors.fill: parent
onClicked: {
Hyprland.dispatch("workspace " + modelData.id)
MouseArea {
anchors.fill: parent
onClicked: {
Hyprland.dispatch("workspace " + modelData.id)
}
}
}
}
+186
View File
@@ -0,0 +1,186 @@
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import qs.Config
import qs.Helpers
Item {
id: root
required property ShellScreen screen
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
readonly property real nonAnimHeight: hasCurrent ? children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight : 0
readonly property Item current: content.item?.current ?? null
property string currentName
property real currentCenter
property bool hasCurrent
property string detachedMode
property string queuedMode
readonly property bool isDetached: detachedMode.length > 0
property int animLength: 400
property list<real> animCurve: MaterialEasing.emphasized
function detach(mode: string): void {
animLength = 600;
if (mode === "winfo") {
detachedMode = mode;
} else {
detachedMode = "any";
queuedMode = mode;
}
focus = true;
}
function close(): void {
hasCurrent = false;
animCurve = MaterialEasing.emphasizedDecel;
animLength = 400;
detachedMode = "";
animCurve = MaterialEasing.emphasized;
}
visible: width > 0 && height > 0
clip: true
implicitWidth: nonAnimWidth
implicitHeight: nonAnimHeight
Keys.onEscapePressed: close()
HyprlandFocusGrab {
active: root.isDetached
windows: [QsWindow.window]
onCleared: root.close()
}
Binding {
when: root.isDetached
target: QsWindow.window
property: "WlrLayershell.keyboardFocus"
value: WlrKeyboardFocus.OnDemand
}
Comp {
id: content
shouldBeActive: root.hasCurrent
asynchronous: true
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: Content {
wrapper: root
}
}
// Comp {
// shouldBeActive: root.detachedMode === "winfo"
// asynchronous: true
// anchors.centerIn: parent
//
// sourceComponent: WindowInfo {
// screen: root.screen
// client: Hypr.activeToplevel
// }
// }
// Comp {
// shouldBeActive: root.detachedMode === "any"
// asynchronous: true
// anchors.centerIn: parent
//
// sourceComponent: ControlCenter {
// screen: root.screen
// active: root.queuedMode
//
// function close(): void {
// root.close();
// }
// }
// }
Behavior on x {
enabled: root.implicitHeight > 0
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
Behavior on y {
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
Behavior on implicitWidth {
enabled: root.implicitHeight > 0
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
Behavior on implicitHeight {
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
component Comp: Loader {
id: comp
property bool shouldBeActive
asynchronous: true
active: false
opacity: 0
states: State {
name: "active"
when: comp.shouldBeActive
PropertyChanges {
comp.opacity: 1
comp.active: true
}
}
transitions: [
Transition {
from: ""
to: "active"
SequentialAnimation {
PropertyAction {
property: "active"
}
Anim {
property: "opacity"
}
}
},
Transition {
from: "active"
to: ""
SequentialAnimation {
Anim {
property: "opacity"
}
PropertyAction {
property: "active"
}
}
}
]
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ Loader {
right: true
bottom: true
}
Background {}
WallBackground {}
}
}
}