From 35524b5e6ee4933d33e51c1c54e5ebf331da3cb7 Mon Sep 17 00:00:00 2001 From: Bloxx12 Date: Sat, 3 May 2025 13:04:07 +0200 Subject: [PATCH] initial commit --- Bar.qml | 37 +++++++++++++ ClockWidget.qml | 39 ++++++++++++++ Launcher.qml | 34 ++++++++++++ ReloadPopup.qml | 127 ++++++++++++++++++++++++++++++++++++++++++++ SysTray.qml | 35 ++++++++++++ WorkspaceWidget.qml | 118 ++++++++++++++++++++++++++++++++++++++++ shell.qml | 22 ++++++++ 7 files changed, 412 insertions(+) create mode 100644 Bar.qml create mode 100644 ClockWidget.qml create mode 100644 Launcher.qml create mode 100644 ReloadPopup.qml create mode 100644 SysTray.qml create mode 100644 WorkspaceWidget.qml create mode 100644 shell.qml diff --git a/Bar.qml b/Bar.qml new file mode 100644 index 0000000..29882b9 --- /dev/null +++ b/Bar.qml @@ -0,0 +1,37 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell + +PanelWindow { + id: root + color: "transparent" + + anchors { + top: true + bottom: true + right: true + } + width: 60 + + ColumnLayout { + // anchors.fill: parent + anchors.margins: 2 + spacing: 2 + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: 2 + } + + ClockWidget {} + + WorkspaceWidget { + bar: root + } + // SysTray { + // bar: root + // } + } +} diff --git a/ClockWidget.qml b/ClockWidget.qml new file mode 100644 index 0000000..450e6a5 --- /dev/null +++ b/ClockWidget.qml @@ -0,0 +1,39 @@ +import QtQuick +import QtQuick.Layouts + +Rectangle { + width: parent.width + height: width + border.color: "black" + border.width: 1 + radius: 5 + // color: "green" + + Item { + width: parent.width + height: text.height * 2 + + anchors.centerIn: parent + + Text { + id: text + anchors.centerIn: parent + property var date: Date() + + text: Qt.formatDateTime(date, "hh\nmm") + + font.family: "Iosevka NF" + font.weight: Font.ExtraBold + font.pointSize: 18 + + color: "black" + } + } + + Timer { + interval: 1000 * 60 + running: true + repeat: true + onTriggered: text.date = new Date() + } +} diff --git a/Launcher.qml b/Launcher.qml new file mode 100644 index 0000000..438658e --- /dev/null +++ b/Launcher.qml @@ -0,0 +1,34 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Widgets +import Quickshell.Services.SystemTray + +Singleton { + PersistentProperties { + id: persist + property bool launcherOpen: false + } + + IpcHandler { + target: "launcher" + + function open(): void { + persist.launcherOpen = true; + } + + function close(): void { + persist.launcherOpen = false; + } + + function toggle(): void { + persist.launcherOpen = !persist.launcherOpen; + } + } +} diff --git a/ReloadPopup.qml b/ReloadPopup.qml new file mode 100644 index 0000000..6f2d834 --- /dev/null +++ b/ReloadPopup.qml @@ -0,0 +1,127 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell + +Scope { + id: root + property bool failed + property string errorString + + // Connect to the Quickshell global to listen for the reload signals. + Connections { + target: Quickshell + + function onReloadCompleted() { + root.failed = false; + popupLoader.loading = true; + } + + function onReloadFailed(error: string) { + // Close any existing popup before making a new one. + popupLoader.active = false; + + root.failed = true; + root.errorString = error; + popupLoader.loading = true; + } + } + + // Keep the popup in a loader because it isn't needed most of the timeand will take up + // memory that could be used for something else. + LazyLoader { + id: popupLoader + + PanelWindow { + id: popup + + anchors { + top: true + right: true + } + + margins { + top: 25 + left: 25 + } + + width: rect.width + height: rect.height + + // color blending is a bit odd as detailed in the type reference. + color: "transparent" + + Rectangle { + id: rect + color: root.failed ? "#40802020" : "#40009020" + + implicitHeight: layout.implicitHeight + 50 + implicitWidth: layout.implicitWidth + 30 + + // Fills the whole area of the rectangle, making any clicks go to it, + // which dismiss the popup. + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: popupLoader.active = false + + // makes the mouse area track mouse hovering, so the hide animation + // can be paused when hovering. + hoverEnabled: true + } + + ColumnLayout { + id: layout + anchors { + top: parent.top + topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + + Text { + text: root.failed ? "Reload failed." : "Reloaded completed!" + color: "white" + } + + Text { + text: root.errorString + color: "white" + // When visible is false, it also takes up no space. + visible: root.errorString != "" + } + } + + // A progress bar on the bottom of the screen, showing how long until the + // popup is removed. + Rectangle { + id: bar + color: "#20ffffff" + anchors.bottom: parent.bottom + anchors.left: parent.left + height: 20 + + PropertyAnimation { + id: anim + target: bar + property: "width" + from: rect.width + to: 0 + duration: failed ? 10000 : 800 + onFinished: popupLoader.active = false + + // Pause the animation when the mouse is hovering over the popup, + // so it stays onscreen while reading. This updates reactively + // when the mouse moves on and off the popup. + paused: mouseArea.containsMouse + } + } + + // We could set `running: true` inside the animation, but the width of the + // rectangle might not be calculated yet, due to the layout. + // In the `Component.onCompleted` event handler, all of the component's + // properties and children have been initialized. + Component.onCompleted: anim.start() + } + } + } +} diff --git a/SysTray.qml b/SysTray.qml new file mode 100644 index 0000000..8889b4e --- /dev/null +++ b/SysTray.qml @@ -0,0 +1,35 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects +import Quickshell +import Quickshell.Services.SystemTray + +Rectangle { + id: root + required property var bar + + Column { + id: column + spacing: 5 + + anchors { + fill: parent + margins: 5 + } + + Repeater { + model: SystemTray.items + + Item { + id: item + + required property SystemTrayItem modelData + + implicitHeight: width + + } + } + } +} diff --git a/WorkspaceWidget.qml b/WorkspaceWidget.qml new file mode 100644 index 0000000..171113c --- /dev/null +++ b/WorkspaceWidget.qml @@ -0,0 +1,118 @@ +// A workspace indicator. +// This is in big parts taken from outfoxxed, the creator of quickshell. +// Please make sure to check out his setup. +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell.Hyprland + +Rectangle { + id: root + required property var bar + + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) + + // Amount of workspaces + property int wsCount: 10 + // Base index for the workspaces + property int wsBaseIndex: 1 + + // index of the current workspace + property int currentIndex: 0 + // count how many workspaces currently exist + property int existsCount: 0 + + implicitHeight: column.implicitHeight + 10 + + width: parent.width + height: width + border.color: "black" + // color: "blue" + border.width: 1 + radius: 5 + + // destructor takes care of nulling + signal workspaceAdded(workspace: HyprlandWorkspace) + + Column { + id: column + spacing: 0 + anchors { + fill: parent + margins: 5 + } + + Repeater { + model: root.wsCount + + Item { + id: wsItem + implicitHeight: 15 + + anchors { + right: parent.right + left: parent.left + } + + // index of the current workspace + required property int index + property int wsIndex: root.wsBaseIndex + index + + property HyprlandWorkspace workspace: null + // check if workspace exists + property bool exists: workspace != null + + property bool active: (root.monitor?.activeWorkspace ?? false) && root.monitor.activeWorkspace == workspace + + Connections { + target: root + + function onWorkspaceAdded(workspace: HyprlandWorkspace) { + if (workspace.id == wsItem.wsIndex) { + wsItem.workspace = workspace; + } + } + } + + property real animActive: active ? 1 : 0.65 + Behavior on animActive { + NumberAnimation { + duration: 150 + } + } + + property real animExists: exists ? 1 : 0 + Behavior on animExists { + NumberAnimation { + duration: 100 + } + } + + Rectangle { + anchors.centerIn: parent + height: 10 + // width: (wsItem.active ? parent.width : parent.width - 20) + width: parent.width * wsItem.animActive + radius: height / 2 + border.color: "black" + border.width: 1 + color: "black" + } + } + } + } + + Connections { + target: Hyprland.workspaces + + function onObjectInsertedPost(workspace) { + root.workspaceAdded(workspace); + } + } + + Component.onCompleted: { + Hyprland.workspaces.values.forEach(workspace => { + root.workspaceAdded(workspace); + }); + } +} diff --git a/shell.qml b/shell.qml new file mode 100644 index 0000000..b87f084 --- /dev/null +++ b/shell.qml @@ -0,0 +1,22 @@ +import Quickshell +import Quickshell.Io +import QtQuick +import QtQuick.Layouts + +ShellRoot { + id: shellroot + + Variants { + model: Quickshell.screens + + Scope { + property var modelData + + Bar { + screen: modelData + } + + ReloadPopup {} + } + } +}