From 91a4f95fd0748863f70c070f2f98a98649d3e0f4 Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Mon, 24 Nov 2025 22:00:50 +0100 Subject: [PATCH] major updates --- Bar.qml | 169 +++++++++++++------------ Config/BarConfig.qml | 55 +++++++++ Config/Config.qml | 2 + Drawers/Backgrounds.qml | 22 ++++ Drawers/Panels.qml | 33 +++++ Modules/AudioPopup.qml | 66 +++++----- Modules/AudioWidget.qml | 8 -- Modules/Background.qml | 103 +++++++--------- Modules/Bar/BarLoader.qml | 165 +++++++++++++++++++++++++ Modules/Clock.qml | 17 +++ Modules/Content.qml | 133 ++++++++++++++++++++ Modules/MaterialEasing.qml | 2 - Modules/NotifBell.qml | 28 +++++ Modules/ResourceDetail.qml | 5 +- Modules/ResourcePopout.qml | 61 ++++++++++ Modules/Resources.qml | 107 ---------------- Time.qml => Modules/Time.qml | 0 Modules/TrayItem.qml | 45 +------ Modules/TrayMenu.qml | 23 +++- Modules/TrayMenuPopout.qml | 230 +++++++++++++++++++++++++++++++++++ Modules/TrayWidget.qml | 32 ++--- Modules/UpdatesWidget.qml | 2 +- Modules/WallBackground.qml | 79 ++++++++++++ Modules/Workspaces.qml | 159 ++++++++++++------------ Modules/Wrapper.qml | 186 ++++++++++++++++++++++++++++ Wallpaper.qml | 2 +- 26 files changed, 1287 insertions(+), 447 deletions(-) create mode 100644 Config/BarConfig.qml create mode 100644 Drawers/Backgrounds.qml create mode 100644 Drawers/Panels.qml create mode 100644 Modules/Bar/BarLoader.qml create mode 100644 Modules/Clock.qml create mode 100644 Modules/Content.qml create mode 100644 Modules/NotifBell.qml create mode 100644 Modules/ResourcePopout.qml rename Time.qml => Modules/Time.qml (100%) create mode 100644 Modules/TrayMenuPopout.qml create mode 100644 Modules/WallBackground.qml create mode 100644 Modules/Wrapper.qml diff --git a/Bar.qml b/Bar.qml index 27782b8..08a4137 100644 --- a/Bar.qml +++ b/Bar.qml @@ -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 + } } } } diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml new file mode 100644 index 0000000..e9a74f4 --- /dev/null +++ b/Config/BarConfig.qml @@ -0,0 +1,55 @@ +import Quickshell.Io + +JsonObject { + property Popouts popouts: Popouts {} + + property list 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 + } +} diff --git a/Config/Config.qml b/Config/Config.qml index f1a0497..d2c1b55 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -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 {} } } } diff --git a/Drawers/Backgrounds.qml b/Drawers/Backgrounds.qml new file mode 100644 index 0000000..31a8c07 --- /dev/null +++ b/Drawers/Backgrounds.qml @@ -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 + } +} diff --git a/Drawers/Panels.qml b/Drawers/Panels.qml new file mode 100644 index 0000000..552a3ea --- /dev/null +++ b/Drawers/Panels.qml @@ -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); + } + } +} diff --git a/Modules/AudioPopup.qml b/Modules/AudioPopup.qml index 1af0650..7559f1d 100644 --- a/Modules/AudioPopup.qml +++ b/Modules/AudioPopup.qml @@ -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"]); } } diff --git a/Modules/AudioWidget.qml b/Modules/AudioWidget.qml index b5347c4..254c323 100644 --- a/Modules/AudioWidget.qml +++ b/Modules/AudioWidget.qml @@ -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 { diff --git a/Modules/Background.qml b/Modules/Background.qml index ab5041e..c43e7ab 100644 --- a/Modules/Background.qml +++ b/Modules/Background.qml @@ -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 {} } } diff --git a/Modules/Bar/BarLoader.qml b/Modules/Bar/BarLoader.qml new file mode 100644 index 0000000..ea6cd99 --- /dev/null +++ b/Modules/Bar/BarLoader.qml @@ -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 + } +} diff --git a/Modules/Clock.qml b/Modules/Clock.qml new file mode 100644 index 0000000..d6dc607 --- /dev/null +++ b/Modules/Clock.qml @@ -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 {} + } + } +} diff --git a/Modules/Content.qml b/Modules/Content.qml new file mode 100644 index 0000000..3b590e3 --- /dev/null +++ b/Modules/Content.qml @@ -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" + } + } + } + ] + } +} diff --git a/Modules/MaterialEasing.qml b/Modules/MaterialEasing.qml index 846da9f..7fba813 100644 --- a/Modules/MaterialEasing.qml +++ b/Modules/MaterialEasing.qml @@ -4,8 +4,6 @@ import Quickshell Singleton { id: root - // thanks to Soramane :> - // expressive curves => thanks end cutie ;) readonly property list emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] readonly property list emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] readonly property int emphasizedAccelTime: 200 diff --git a/Modules/NotifBell.qml b/Modules/NotifBell.qml new file mode 100644 index 0000000..cb32edd --- /dev/null +++ b/Modules/NotifBell.qml @@ -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 + } + } + } +} diff --git a/Modules/ResourceDetail.qml b/Modules/ResourceDetail.qml index 41b3688..a687fe0 100644 --- a/Modules/ResourceDetail.qml +++ b/Modules/ResourceDetail.qml @@ -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 diff --git a/Modules/ResourcePopout.qml b/Modules/ResourcePopout.qml new file mode 100644 index 0000000..c38d378 --- /dev/null +++ b/Modules/ResourcePopout.qml @@ -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 )) + } + } +} diff --git a/Modules/Resources.qml b/Modules/Resources.qml index 9c43b6d..3afd5e9 100644 --- a/Modules/Resources.qml +++ b/Modules/Resources.qml @@ -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(); - } - } - } } diff --git a/Time.qml b/Modules/Time.qml similarity index 100% rename from Time.qml rename to Modules/Time.qml diff --git a/Modules/TrayItem.qml b/Modules/TrayItem.qml index a26ee09..7d896cc 100644 --- a/Modules/TrayItem.qml +++ b/Modules/TrayItem.qml @@ -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; - } } } diff --git a/Modules/TrayMenu.qml b/Modules/TrayMenu.qml index 4e9abe6..7555770 100644 --- a/Modules/TrayMenu.qml +++ b/Modules/TrayMenu.qml @@ -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 diff --git a/Modules/TrayMenuPopout.qml b/Modules/TrayMenuPopout.qml new file mode 100644 index 0000000..e018ec1 --- /dev/null +++ b/Modules/TrayMenuPopout.qml @@ -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 {} + } +} diff --git a/Modules/TrayWidget.qml b/Modules/TrayWidget.qml index c014ab5..8572960 100644 --- a/Modules/TrayWidget.qml +++ b/Modules/TrayWidget.qml @@ -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 } } } diff --git a/Modules/UpdatesWidget.qml b/Modules/UpdatesWidget.qml index 2ce264c..6220ac1 100644 --- a/Modules/UpdatesWidget.qml +++ b/Modules/UpdatesWidget.qml @@ -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" diff --git a/Modules/WallBackground.qml b/Modules/WallBackground.qml new file mode 100644 index 0000000..ab5041e --- /dev/null +++ b/Modules/WallBackground.qml @@ -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 + } + } + } +} diff --git a/Modules/Workspaces.qml b/Modules/Workspaces.qml index 6397a96..938e156 100644 --- a/Modules/Workspaces.qml +++ b/Modules/Workspaces.qml @@ -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) + } } } } diff --git a/Modules/Wrapper.qml b/Modules/Wrapper.qml new file mode 100644 index 0000000..032e5fe --- /dev/null +++ b/Modules/Wrapper.qml @@ -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 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" + } + } + } + ] + } +} diff --git a/Wallpaper.qml b/Wallpaper.qml index c0a651d..af1f9ff 100644 --- a/Wallpaper.qml +++ b/Wallpaper.qml @@ -26,7 +26,7 @@ Loader { right: true bottom: true } - Background {} + WallBackground {} } } }