Merge branch 'new-one'

This commit is contained in:
2026-06-26 21:06:14 +01:00
39 changed files with 2054 additions and 824 deletions

11
.gitignore vendored
View File

@@ -1 +1,12 @@
.session .session
# Devenv
.devenv*
devenv.local.nix
devenv.local.yaml
# direnv
.direnv
# pre-commit
.pre-commit-config.yaml

95
Bar.qml
View File

@@ -1,10 +1,10 @@
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import "Components/Color.js" as Colors import "Bar"
import "Color.js" as Colors
import "Components"
Scope { Scope {
Variants { Variants {
@@ -14,32 +14,28 @@ Scope {
PanelWindow { PanelWindow {
id: win id: win
property double barExclusionZone: 15
property double barMaxHeight: 1000
required property var modelData required property var modelData
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
color: "transparent" color: "transparent"
exclusionMode: ExclusionMode.Normal exclusionMode: ExclusionMode.Normal
exclusiveZone: 20 exclusiveZone: barExclusionZone
implicitHeight: 1000 implicitHeight: barMaxHeight
screen: modelData screen: modelData
Behavior on implicitHeight {
NumberAnimation {
duration: 0
easing.type: Easing.InOutQuad
}
}
mask: Region { mask: Region {
Region { Region {
item: workspaceSwitcher item: left
} }
Region { Region {
item: pillItem item: center
} }
Region { Region {
item: utilsItem item: right
} }
} }
@@ -49,35 +45,54 @@ Scope {
top: true top: true
} }
Workspace {
id: workspaceSwitcher
monitor: win.monitor
}
RectangularShadow {
anchors.fill: pillItem
blur: 20
color: Colors.crust
offset.x: 3
offset.y: 3
radius: pillItem.radius
spread: 7
}
Pill {
id: pillItem
monitor: win.monitor
}
Item { Item {
anchors.fill: parent anchors {
fill: parent
topMargin: 3
}
Utils { Row {
id: utilsItem id: left
monitor: win.monitor height: childrenRect.height
anchors {
left: parent.left
leftMargin: 7
}
Left {
monitor: win.monitor
}
}
Row {
id: center
height: childrenRect.height
anchors {
horizontalCenter: parent.horizontalCenter
}
Center {
monitor: win.monitor
}
}
Row {
id: right
height: childrenRect.height
anchors {
right: parent.right
rightMargin: 7
}
Right {
monitor: win.monitor
}
} }
} }
} }

264
Bar/Center.qml Normal file
View File

@@ -0,0 +1,264 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.Mpris
import Quickshell.Services.Notifications
import "../Color.js" as Colors
import "../Components/"
import "../Services/"
import "./Center"
Container {
id: root
property Notification latestNotification: NotificationManager.getLatestNotification() || null
required property HyprlandMonitor monitor
property NotificationServer notifServer: NotificationManager.notif
property string previousState
boxColor: Colors.mauve
defaultItem: time
exclusiveMonitor: root.monitor
exclusiveToScreen: true
states: [
State {
name: ""
PropertyChanges {
root.boxHeight: 28
root.boxRadius: 9
root.boxWidth: 100
root.visibleTopMargin: 0
}
StateChangeScript {
script: {
if (root.previousState != "hovered") {
root.stack.replace(time);
}
root.previousState = "";
}
}
},
State {
name: "hovered"
PropertyChanges {
root.boxHeight: 35
root.boxRadius: 15
root.boxWidth: 110
root.visibleTopMargin: 0
}
StateChangeScript {
script: {
root.previousState = "hovered";
}
}
},
State {
name: "mprisToast"
PropertyChanges {
root.boxHeight: 35
root.boxRadius: 15
root.boxWidth: 350
root.visibleTopMargin: 0
}
StateChangeScript {
script: {
root.previousState = "mpris";
root.stack.replace(mpris);
}
}
},
State {
name: "expanded"
PropertyChanges {
root.boxHeight: 140
root.boxRadius: 30
root.boxWidth: 300
root.visibleTopMargin: 0
}
StateChangeScript {
script: {
if (root.state != "notifications") {
root.stack.replace(fullTime);
root.previousState = "expanded";
}
}
}
},
State {
extend: "expanded"
name: "notifications"
PropertyChanges {
root.boxHeight: 320
}
},
State {
name: "notified"
PropertyChanges {
root.boxHeight: 90
root.boxRadius: 30
root.boxWidth: 330
root.visibleTopMargin: 20
}
StateChangeScript {
script: {
root.stack.replace(notification);
root.previousState = "notified";
}
}
}
]
hover.onHoveredChanged: {
if (hover.hovered == true) {
if (root.state == "notified") {
root.state = "notified";
notificationViewTimer.stop();
} else if (MprisManager.getPlaying() == true) {
root.state = "mprisToast";
} else {
root.state = "hovered";
}
} else {
root.state = "";
}
}
onLatestNotificationChanged: {
if (latestNotification != null && Hyprland.focusedMonitor == monitor) {
if (root.latestNotification.lastGeneration == false) {
root.state = "notified";
notificationViewTimer.start();
}
}
}
tap.onTapped: {
root.state == "hovered" ? root.state = "expanded" : undefined;
}
Connections {
function onTrackChanged() {
if (Hyprland.focusedMonitor == root.monitor) {
musicToastTimer.restart();
if (root.state == "") {
root.state = "mprisToast";
}
}
}
target: MprisManager.defaultPlayer
}
Timer {
id: notificationViewTimer
interval: 3000
repeat: false
running: false
onTriggered: {
root.state = "";
}
}
Timer {
id: musicToastTimer
interval: 3000
repeat: false
running: false
onTriggered: {
if (!root.hover.hovered) {
console.log("some");
root.state = "";
}
}
}
Component {
id: time
Item {
CenteredText {
text: Time.time
}
}
}
Component {
id: mpris
Mpris {
TapHandler {
onTapped: root.state = "expanded"
}
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: MprisManager.skip()
}
TapHandler {
acceptedButtons: Qt.MiddleButton
onTapped: MprisManager.prev()
}
}
}
Component {
id: fullTime
Expanded {
NotificationList {
id: notifList
animEnabled: false
radius: root.boxRadius
rootState: root.state
server: root.notifServer
state: ""
TapHandler {
onTapped: {
notifList.animEnabled = true;
root.state = "notifications";
Qt.callLater(() => {
notifList.animEnabled = false;
});
}
}
}
}
}
Component {
id: notification
NotificationDisplay {
body: root.latestNotification.body
summary: root.latestNotification.summary
TapHandler {
onTapped: {
root.state = "notifications";
root.stack.replace(fullTime);
}
}
}
}
}

55
Bar/Center/Expanded.qml Normal file
View File

@@ -0,0 +1,55 @@
import QtQuick
import Quickshell
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: fullTimeRoot
Rectangle {
anchors.fill: parent
radius: 30
gradient: MidpointGradient {
color: Colors.red
}
}
Item {
id: timeColumnContainer
anchors.fill: parent
Column {
id: timeColumn
spacing: 5
anchors {
centerIn: parent
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
color: Colors.surface0
text: Time.time
font {
pointSize: 30
}
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
color: Colors.surface1
text: Time.date
font {
pointSize: 9
weight: 400
}
}
}
}
}

62
Bar/Center/Mpris.qml Normal file
View File

@@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.Mpris
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: root
property MprisPlayer player: MprisManager.defaultPlayer
Rectangle {
color: Colors.red
radius: 5
anchors {
bottomMargin: 15
fill: parent
leftMargin: 10
rightMargin: parent.width - ((parent.width * MprisManager.pos) - 10)
topMargin: 15
}
}
RowLayout {
anchors {
bottomMargin: 2
fill: parent
leftMargin: 7
rightMargin: 7
topMargin: 2
}
Item {
Layout.fillHeight: true
Layout.preferredWidth: height
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: MprisManager.defaultPlayer.trackArtUrl
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
StyledText {
elide: Qt.ElideRight
horizontalAlignment: Qt.AlignHCenter
text: MprisManager.defaultPlayer.trackArtist + " - " + MprisManager.defaultPlayer.trackTitle
verticalAlignment: Qt.AlignVCenter
anchors {
fill: parent
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
import QtQuick
import Quickshell
import Quickshell.Services.Notifications
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: root
required property string body
required property string summary
Rectangle {
anchors.fill: parent
radius: 30
gradient: MidpointGradient {
color: Colors.green
midpoint: 0.7
}
}
Column {
anchors.centerIn: parent
spacing: 5
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 11
text: root.summary
}
StyledText {
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: root.body
verticalAlignment: Text.AlignVCenter
width: root.width - 20
font {
pointSize: 10
weight: 500
}
}
}
}

View File

@@ -0,0 +1,307 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Services.Notifications
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: root
required property bool animEnabled
required property double radius
required property string rootState
required property NotificationServer server
Behavior on anchors.topMargin {
enabled: root.animEnabled
SpringAnimation {
damping: 0.35
spring: 4
}
}
states: [
State {
name: ""
when: root.rootState != "notifications"
PropertyChanges {
root.anchors.topMargin: root.parent.height - 21
}
},
State {
name: "expanded"
when: root.rootState == "notifications"
PropertyChanges {
root.anchors.topMargin: 5
}
}
]
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
top: parent.top
topMargin: parent.height - 21
}
Rectangle {
id: background
anchors.fill: parent
color: Colors.surface0
radius: root.radius - 3
states: [
State {
name: ""
when: root.rootState != "notifications"
PropertyChanges {}
},
State {
name: "expanded"
when: root.rootState == "notifications"
PropertyChanges {
background.anchors.bottomMargin: 5
background.anchors.leftMargin: 5
background.anchors.rightMargin: 5
}
}
]
anchors {
bottomMargin: 15
leftMargin: 20
rightMargin: 20
}
}
Item {
id: notifListContainer
anchors.fill: parent
clip: true
opacity: 0
anchors {
bottomMargin: 5
leftMargin: 5
rightMargin: 5
topMargin: 5
}
CenteredText {
color: Colors.overlay0
text: "No Notifications"
visible: root.server.trackedNotifications.values.length <= 0
}
ListView {
id: notifScrollingList
anchors.fill: parent
boundsBehavior: Flickable.StopAtBounds
height: Math.min(contentHeight, parent.height)
implicitHeight: background.height
model: root.server.trackedNotifications.values.length
spacing: 7
delegate: Item {
id: notifRoot
property Notification currentNotif: root.server.trackedNotifications.values[(notifRoot.notifLen
- 1) - index]
required property real index
property real notifLen: root.server.trackedNotifications.values.length
height: 50
width: ListView.view.width
states: [
State {
name: ""
when: notifHover.hovered == false
},
State {
name: "hovered"
when: notifHover.hovered == false
PropertyChanges {}
}
]
HoverHandler {
id: notifHover
}
Rectangle {
id: dismissButton
color: Colors.red
implicitWidth: 100
radius: 10
Behavior on anchors.rightMargin {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
when: notifHover.hovered == false
},
State {
name: "hovered"
when: notifHover.hovered == true
PropertyChanges {
dismissButton.anchors.rightMargin: 5
}
}
]
StyledText {
color: Colors.surface0
font.pointSize: 15
horizontalAlignment: Qt.AlignRight
text: "󰩹"
verticalAlignment: Qt.AlignVCenter
anchors {
fill: parent
rightMargin: 14
}
}
TapHandler {
onTapped: {
notifRoot.currentNotif.dismiss();
}
}
anchors {
bottom: parent.bottom
bottomMargin: 3
right: parent.right
rightMargin: 10
top: parent.top
topMargin: 3
}
}
Rectangle {
id: textContainer
anchors.fill: parent
clip: true
color: Colors.surface2
radius: 9
Behavior on anchors.rightMargin {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
when: notifHover.hovered == false
},
State {
name: "hovered"
when: notifHover.hovered == true
PropertyChanges {
textContainer.anchors.rightMargin: 40
}
}
]
Column {
id: textColumn
anchors {
centerIn: parent
left: parent.left
right: parent.right
}
StyledText {
color: Colors.text
horizontalAlignment: Text.AlignHCenter
text: notifRoot.currentNotif.summary
verticalAlignment: Text.AlignVCenter
width: notifRoot.width - 10
}
StyledText {
color: Colors.subtext1
horizontalAlignment: Text.AlignHCenter
text: notifRoot.currentNotif.body
verticalAlignment: Text.AlignVCenter
width: notifRoot.width - 10
}
}
}
}
anchors {
bottomMargin: 5
leftMargin: 10
rightMargin: 10
topMargin: 5
}
}
}
MultiEffect {
id: notifListClipper
anchors.fill: notifListContainer
maskEnabled: true
maskSource: clipsource
opacity: 0
source: notifListContainer
Behavior on opacity {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
when: root.rootState != "notifications"
PropertyChanges {
notifListClipper.opacity: 0
}
},
State {
name: "expanded"
when: root.rootState == "notifications"
PropertyChanges {
notifListClipper.opacity: 1
}
}
]
}
Rectangle {
id: clipsource
anchors.fill: background
color: "black"
layer.enabled: true
radius: background.radius
visible: false
}
}

70
Bar/Left.qml Normal file
View File

@@ -0,0 +1,70 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.Notifications
import "../Color.js" as Colors
import "../Components/"
import "../Services/"
import "./Center"
import "./Left/"
Container {
id: root
property double extraHoveredWidth: 0
required property HyprlandMonitor monitor
property double rowLeftMargin: 4
property double rowSpacing: 3
property double rowYmargin: 3
property double selectorFocusedExtra: 15
property double selectorHeight: 20
property double selectorWidth: 20
property double spacing: 3
property double workspaceNumber: WorkspaceManager.getNumberOfWorkspaces(monitor)
boxHeight: 25
boxWidth: (selectorWidth * workspaceNumber) + ((2 * rowLeftMargin) + (rowSpacing * (
workspaceNumber - 1))) + extraHoveredWidth + (hoverHandler.hovered == true
? selectorFocusedExtra : 0)
exclusiveMonitor: monitor
defaultItem: WorkspaceContainer {
extraHoveredWidth: root.extraHoveredWidth
monitor: root.monitor
rowLeftMargin: root.rowLeftMargin
rowSpacing: root.rowSpacing
rowYmargin: root.rowYmargin
selectorFocusedExtra: root.selectorFocusedExtra
selectorHeight: root.selectorHeight
selectorWidth: root.selectorWidth
}
Behavior on rowYmargin {
SpringAnimation {
damping: 0.3
spring: 4
}
}
states: [
State {
name: ""
when: !hoverHandler.hovered
},
State {
name: "hovered"
when: hoverHandler.hovered
PropertyChanges {
root.boxHeight: 40
root.extraHoveredWidth: 25
root.rowYmargin: 5
root.selectorWidth: 40
}
}
]
HoverHandler {
id: hoverHandler
}
}

View File

@@ -0,0 +1,86 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: root
required property double extraHoveredWidth
required property HyprlandMonitor monitor
required property double rowLeftMargin
required property double rowSpacing
required property double rowYmargin
required property double selectorFocusedExtra
required property double selectorHeight
required property double selectorWidth
Item {
id: newWorkspaceButton
implicitWidth: 20
opacity: root.extraHoveredWidth > 0 ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: 100
}
}
TapHandler {
id: tapHandler
onTapped: WorkspaceManager.activateNextFreeWorkspace()
}
anchors {
bottom: parent.bottom
bottomMargin: 5 + root.rowYmargin
right: parent.right
rightMargin: 4
top: parent.top
topMargin: 5 + root.rowYmargin
}
Rectangle {
anchors.fill: parent
color: Colors.green
radius: 4
StyledText {
anchors.centerIn: parent
text: "+"
}
}
}
Row {
spacing: root.rowSpacing
anchors {
bottom: parent.bottom
bottomMargin: root.rowYmargin
left: parent.left
leftMargin: root.rowLeftMargin
top: parent.top
topMargin: root.rowYmargin
}
Repeater {
model: WorkspaceManager.getWorkspacesForMonitor(root.monitor)
WorkspaceSelector {
required property HyprlandWorkspace modelData
extraHoveredWidth: root.extraHoveredWidth
selectorFocusedExtra: root.selectorFocusedExtra
selectorHeight: root.selectorHeight
selectorWidth: root.selectorWidth
workspace: modelData
}
}
}
}

View File

@@ -0,0 +1,87 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
Item {
id: root
required property double extraHoveredWidth
property bool hovered: extraHoveredWidth > 0 ? true : false
property color selectorColor: Colors.surface2
required property double selectorFocusedExtra
required property double selectorHeight
required property double selectorWidth
required property HyprlandWorkspace workspace
property string workspaceState: WorkspaceManager.getWorkspaceState(workspace)
implicitWidth: selectorWidth + (root.hovered == true ? (root.workspaceState == "focused"
? selectorFocusedExtra : 0) : 0)
Behavior on implicitWidth {
SpringAnimation {
damping: 0.3
spring: 4
}
}
states: [
State {
name: ""
when: root.workspaceState == "inactive"
PropertyChanges {
root.selectorColor: Colors.surface2
}
},
State {
name: "active"
when: root.workspaceState == "active"
PropertyChanges {
root.selectorColor: Colors.overlay2
}
},
State {
name: "focused"
when: root.workspaceState == "focused"
PropertyChanges {
root.selectorColor: Colors.lavender
}
}
]
TapHandler {
id: tapHandler
onTapped: root.workspace.activate()
}
anchors {
bottom: parent.bottom
top: parent.top
}
Rectangle {
color: root.selectorColor
radius: 6
Behavior on color {
ColorAnimation {
duration: 100
}
}
anchors {
fill: parent
}
CenteredText {
font.pointSize: 10.5
text: root.workspace.id
}
}
}

21
Bar/Right.qml Normal file
View File

@@ -0,0 +1,21 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.Notifications
import "../Color.js" as Colors
import "../Components/"
import "../Services/"
import "./Center"
import "./Right/"
Row {
id: root
required property HyprlandMonitor monitor
Bluetooth {
exclusiveMonitor: root.monitor
}
}

72
Bar/Right/Bluetooth.qml Normal file
View File

@@ -0,0 +1,72 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Bluetooth
import "../../Color.js" as Colors
import "../../Components/"
import "../../Services/"
import "Bluetooth"
Container {
id: root
animOffset: 200
boxColor: Colors.peach
boxHeight: 25
boxWidth: 30
defaultItem: icon
exclusiveToScreen: true
forceHidden: BluetoothManager.getConnected()
state: ""
states: [
State {
name: ""
PropertyChanges {
root.boxHeight: 25
root.boxWidth: 30
}
StateChangeScript {
script: {
root.stack.replace(icon);
}
}
},
State {
name: "expanded"
PropertyChanges {
root.boxHeight: 250
root.boxRadius: 20
root.boxWidth: 350
}
StateChangeScript {
script: {
root.stack.replace(expanded);
}
}
}
]
hover.onHoveredChanged: hover.hovered == false ? state = "" : undefined
tap.onTapped: state = "expanded"
Component {
id: icon
Item {
CenteredText {
text: "󰂱"
}
}
}
Component {
id: expanded
Expanded {}
}
}

View File

@@ -0,0 +1,384 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Bluetooth
import "../../../Color.js" as Colors
import "../../../Components/"
import "../../../Services/"
Item {
id: root
property double topBarHeight: 35
Rectangle {
color: Colors.surface0
radius: 17
anchors {
bottomMargin: 5
fill: parent
leftMargin: 5
rightMargin: 5
topMargin: 5
}
Rectangle {
id: topBar
property double topBarHeight: root.topBarHeight
color: Colors.peach
implicitHeight: topBarHeight
radius: 9
anchors {
left: parent.left
leftMargin: 5
right: parent.right
rightMargin: 5
top: parent.top
topMargin: 5
}
RowLayout {
id: topBarRow
property double topBarRowPadding: 4
spacing: 3
anchors {
left: parent.left
leftMargin: topBarRowPadding
right: parent.right
rightMargin: topBarRowPadding
top: parent.top
topMargin: topBarRowPadding
}
Rectangle {
Layout.fillWidth: true
// color: Qt.lighter(Colors.peach, 1.2)
color: Colors.surface1
implicitHeight: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2)
radius: 6
StyledText {
anchors.verticalCenter: parent.verticalCenter
color: Colors.text
horizontalAlignment: Qt.AlignLeft
text: " " + BluetoothManager.defaultAdapterName
}
}
Rectangle {
id: scanButton
color: Qt.lighter(Colors.peach, 1.15)
implicitHeight: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2)
implicitWidth: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2)
radius: 6
Behavior on color {
ColorAnimation {
duration: 100
}
}
states: [
State {
name: "hovered"
PropertyChanges {
scanButton.color: Colors.green
}
}
]
border {
color: Colors.surface0
width: 2
}
HoverHandler {
onHoveredChanged: hovered == true ? scanButton.state = "hovered" : scanButton.state = ""
}
TapHandler {
onTapped: {
BluetoothManager.toggleDiscover();
}
}
CenteredText {
id: scanButtonText
text: ""
NumberAnimation {
id: animateRotation
duration: 1000
from: 0
loops: Animation.Infinite
properties: "rotation"
running: BluetoothManager.defaultAdapter.discovering
target: scanButtonText
to: 360
onStopped: {
scanButton.rotation = 0;
}
}
}
}
}
}
ListView {
boundsBehavior: Flickable.StopAtBounds
clip: true
model: BluetoothManager.getDevicesList()
spacing: 5
delegate: Item {
id: deviceRoot
required property real index
required property BluetoothDevice modelData
height: 55
width: ListView.view.width
Rectangle {
id: containerBox
anchors.fill: parent
clip: true
color: Colors.peach
radius: 7
Rectangle {
anchors.centerIn: parent
color: Colors.green
implicitHeight: parent.height - 10
implicitWidth: parent.width * (deviceRoot.modelData.batteryAvailable == true
? deviceRoot.modelData.battery : 0) - 10
opacity: 0.7
radius: 7
visible: deviceRoot.modelData.batteryAvailable == true
}
RowLayout {
anchors {
bottomMargin: 4
fill: parent
leftMargin: 4
rightMargin: 4
topMargin: 4
}
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: height
color: deviceRoot.modelData.connected ? Colors.green : Qt.lighter(Colors.peach, 1.25)
radius: 5
CenteredText {
color: Colors.surface0
font.pointSize: 30
text: BluetoothManager.getIcon(deviceRoot.modelData.icon)
}
}
ColumnLayout {
Layout.fillWidth: true
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
color: Qt.lighter(Colors.peach, 1.15)
radius: 4
StyledText {
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
elide: Qt.ElideRight
text: deviceRoot.modelData.name
}
}
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
StyledText {
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
color: Colors.surface2
font.weight: 300
text: deviceRoot.modelData.address
}
}
}
Item {
Layout.fillHeight: true
Layout.preferredWidth: height
RowLayout {
anchors.fill: parent
Rectangle {
id: itemEntry
Layout.fillHeight: true
Layout.fillWidth: true
color: Colors.red
radius: 3
Loader {
id: deleteButtonLoader
anchors.fill: parent
sourceComponent: forgetButton
states: [
State {
name: ""
when: deviceRoot.modelData.connected == false
PropertyChanges {
deleteButtonLoader.sourceComponent: forgetButton
}
},
State {
name: "connected"
when: deviceRoot.modelData.connected == true
PropertyChanges {
deleteButtonLoader.sourceComponent: disconnectButton
}
}
]
}
Component {
id: disconnectButton
Item {
anchors.fill: parent
TapHandler {
onTapped: deviceRoot.modelData.disconnect()
}
CenteredText {
text: ""
}
}
}
Component {
id: forgetButton
Item {
anchors.fill: parent
TapHandler {
onTapped: deviceRoot.modelData.forget()
}
CenteredText {
text: "󰩹"
}
}
}
}
Rectangle {
id: greenButton
property Component loadedItem: connectButton
Layout.fillHeight: true
Layout.fillWidth: true
color: Colors.green
radius: 3
states: [
State {
name: ""
when: deviceRoot.modelData.paired == true
PropertyChanges {
greenButton.color: Colors.green
greenButton.loadedItem: connectButton
}
},
State {
name: "unpaired"
when: deviceRoot.modelData.paired == false
PropertyChanges {
greenButton.color: Colors.blue
greenButton.loadedItem: pairButton
}
}
]
Loader {
anchors.fill: parent
sourceComponent: greenButton.loadedItem
}
Component {
id: connectButton
Item {
anchors.fill: parent
CenteredText {
text: "󱘖"
}
TapHandler {
onTapped: deviceRoot.modelData.connect()
}
}
}
Component {
id: pairButton
Item {
anchors.fill: parent
CenteredText {
text: ""
}
TapHandler {
onTapped: {
deviceRoot.modelData.pair();
}
}
}
}
}
}
}
}
}
}
anchors {
bottomMargin: 5
fill: parent
leftMargin: 5
rightMargin: 5
topMargin: 10 + root.topBarHeight
}
}
}
}

View File

@@ -0,0 +1,7 @@
import QtQuick
import "../Color.js" as Colors
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}

View File

@@ -1,42 +0,0 @@
// Time.qml
// with this line our type becomes a Singleton
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
// your singletons should always have Singleton as the type
Singleton {
id: root
property string time
property string day
Process {
id: timeProc
command: ["date", "+%T"]
running: true
stdout: StdioCollector {
onStreamFinished: root.time = this.text
}
}
Process {
id: dateProc
command: ["date", "+%A %B %d %Y"]
running: true
stdout: StdioCollector {
onStreamFinished: root.day = this.text
}
}
Timer {
interval: 500
running: true
repeat: true
onTriggered: timeProc.running = true, dateProc.running = true
}
}

140
Components/Container.qml Normal file
View File

@@ -0,0 +1,140 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Hyprland
import "../Color.js" as Colors
Item {
id: root
property double animOffset: 0
property color boxColor: Colors.surface0
property double boxHeight: 28
property double boxRadius: 9
property double boxWidth: 100
readonly property double calculatedTopMargin: {
if (root.exclusiveToScreen) {
if (Hyprland.focusedMonitor != root.exclusiveMonitor || forceHidden == true) {
return -4 - root.boxHeight - 5;
} else {
return visibleTopMargin;
}
} else {
return visibleTopMargin;
}
}
property Component defaultItem
required property HyprlandMonitor exclusiveMonitor
property bool exclusiveToScreen: false
property bool forceHidden: false
property alias hover: hoverHandler
property alias rect: container
property alias stack: containerContent
property alias tap: tapHandler
property double visibleTopMargin: 0
implicitHeight: boxHeight
implicitWidth: boxWidth
Behavior on anchors.topMargin {
animation: defaultCurve
}
Behavior on boxHeight {
SpringAnimation {
damping: 0.3
spring: 4
}
}
Behavior on boxRadius {
SpringAnimation {
damping: 0.3
spring: 4
}
}
Behavior on boxWidth {
SpringAnimation {
damping: 0.3
spring: 4
}
}
anchors {
top: parent.top
topMargin: calculatedTopMargin
}
NumberAnimation {
id: defaultCurve
duration: 200 + root.animOffset
easing: Easing.InOutBack
}
HoverHandler {
id: hoverHandler
}
TapHandler {
id: tapHandler
}
RectangularShadow {
anchors.fill: container
blur: 30
color: Colors.mantle
offset.x: 7
offset.y: 3
radius: container.radius
spread: 10
}
Rectangle {
id: container
anchors.fill: parent
clip: true
color: root.boxColor
radius: root.boxRadius
StackView {
id: containerContent
anchors.fill: parent
replaceEnter: Transition {
PropertyAnimation {
duration: 100
from: 0
property: "opacity"
to: 1
}
PropertyAnimation {
duration: 100
from: -5
property: "anchors.topMargin"
to: 0
}
}
replaceExit: Transition {
PropertyAnimation {
duration: 100
from: 1
property: "opacity"
to: 0
}
}
Component.onCompleted: containerContent.push(root.defaultItem)
onCurrentItemChanged: {
if (currentItem) {
// Dynamically center the incoming child to the StackView
currentItem.anchors.horizontalCenter = containerContent.horizontalCenter;
currentItem.anchors.verticalCenter = containerContent.verticalCenter;
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
import QtQuick
Gradient {
id: root
required property color color
property double midpoint: 0.6
GradientStop {
color: "transparent"
position: 0.0
}
GradientStop {
color: "transparent"
position: root.midpoint
}
GradientStop {
color: root.color
position: 1.0
}
}

View File

@@ -1,140 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import "Color.js" as Colors
import "./Pill"
Rectangle {
required property HyprlandMonitor monitor
id: container
state: ""
visible: true
property bool shown: monitor !== null && monitor.focused
Behavior on anchors.topMargin {
SmoothedAnimation { duration: 70; easing.type: Easing.OutBounce }
}
opacity: shown ? 1 : 0
Behavior on opacity {
NumberAnimation {duration: 50}
}
anchors {
top: parent.top
topMargin: shown ? 5 : -implicitHeight
horizontalCenter: parent.horizontalCenter
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: container.state == 'expanded' ? container.state = "" : container.state = 'expanded';
}
implicitWidth: 130
implicitHeight: 30
radius: 30
property real gradientMidpoint: 0.4
gradient: Gradient {
GradientStop { position: 0.0; color: Colors.surface0 }
GradientStop { position: container.gradientMidpoint; color: Colors.surface0 }
GradientStop { position: 1.0; color: Colors.surface2 }
}
border {color: Colors.lavender; width: 2}
component SmoothAnim: SpringAnimation {
spring: 3
mass: 0.5
damping: 0.18
}
Behavior on implicitHeight {
SmoothAnim {}
}
Behavior on implicitWidth {
SmoothAnim {}
}
Behavior on gradientMidpoint {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
StateChangeScript {
script: mainLoader.replace(defaultComponent)
}
},
State {
name: "expanded"
StateChangeScript {
script: mainLoader.replace(expandedComponent)
}
PropertyChanges { container.gradientMidpoint: 0.7}
PropertyChanges { container.implicitHeight: 130}
PropertyChanges { container.implicitWidth: 300}
}
]
StackView {
id: mainLoader
anchors.fill: parent
MouseArea {
id: mouseAreaStack
anchors.fill: parent
hoverEnabled: true
// FIX part 1: Prevent internal StackView items from changing cursor grab
propagateComposedEvents: true
onClicked: {
container.state == 'expanded' ? container.state = "" : container.state = 'expanded';
}
onExited: {
container.state = ""
}
}
enabled:false
initialItem: defaultComponent
// Custom animations for switching components
replaceEnter: Transition {
PropertyAnimation { property: "opacity"; from: 0; to: 1; duration: 100 }
PropertyAnimation { property: "scale"; from: 0.9; to: 1.0; duration: 100 }
}
replaceExit: Transition {
PropertyAnimation { property: "opacity"; from: 1; to: 0; duration: 100 }
}
}
Component {
id: defaultComponent
Default {}
}
Component {
id: expandedComponent
Expanded {}
}
}

View File

@@ -1,16 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Controls
import "../Color.js" as Colors
import "../"
Item {
Text {
font.family: "FiraMono Nerd Font"
anchors.centerIn: parent
font.pixelSize: 15
color: Colors.text
text: Clock.time
}
}

View File

@@ -1,29 +0,0 @@
import Quickshell
import Quickshell.Hyprland
import QtQuick
import QtQuick.Layouts
import "../Color.js" as Colors
import "../"
Item {
anchors.fill: parent
Column {
spacing: 5
anchors.centerIn: parent
Text {
font.family: "FiraMono Nerd Font"
font.pixelSize: 40
color: Colors.text
anchors.horizontalCenter: parent.horizontalCenter
text: Clock.time
}
Text {
font.family: "FiraMono Nerd Font"
color: Colors.subtext0
anchors.horizontalCenter: parent.horizontalCenter
text: Clock.day
}
}
}

13
Components/StyledText.qml Normal file
View File

@@ -0,0 +1,13 @@
import QtQuick
import "../Color.js" as Colors
Text {
color: Colors.base
elide: Text.ElideRight
font {
family: "FiraMono Nerd Font"
pointSize: 12
weight: 600
}
}

View File

@@ -1,45 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "./Utils"
RowLayout {
id: utilsContainer
required property HyprlandMonitor monitor
property bool shown: monitor !== null && monitor.focused
opacity: shown ? 1 : 0
state: ""
Behavior on anchors.topMargin {
SmoothedAnimation {
duration: 300
}
}
Behavior on opacity {
NumberAnimation {
duration: 50
}
}
states: [
State {
name: ""
}
]
anchors {
right: parent.right
rightMargin: 7
top: parent.top
topMargin: shown ? -33 : -40
}
Battery {}
// Mpris {}
}

View File

@@ -1,46 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Services.UPower
import "../Color.js" as Colors
import "./"
Container {
id: batteryRoot
boxColor: Colors.green
boxState: ""
sourceComponent: batteryClosed
visible: UPower.devices[0] ? true : false
MouseArea {
anchors.fill: parent
enabled: true
hoverEnabled: true
propagateComposedEvents: true
onClicked: batteryRoot.boxState == "" ? batteryRoot.boxState = "open" : batteryRoot.boxState = ""
onExited: batteryRoot.boxState = ""
}
Component {
id: batteryClosed
Item {
id: batteryRoot
anchors.fill: parent
Text {
color: Colors.base
text: "something"
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
}
}
}
}

View File

@@ -1,64 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
Item {
id: container
required property color boxColor
property double boxHeight
property double boxRadius
required property string boxState
property double boxWidth
property double openHeight
property double openWidth
required property Component sourceComponent
Layout.topMargin: 0
implicitHeight: boxHeight || 50
implicitWidth: boxWidth || 30
state: boxState
Behavior on anchors.topMargin {
SmoothAnim {}
}
Behavior on implicitHeight {
SmoothAnim {}
}
Behavior on implicitWidth {
SmoothAnim {}
}
states: [
State {
name: ""
},
State {
name: "open"
PropertyChanges {
container.Layout.topMargin: 35
container.implicitHeight: openHeight || 150
container.implicitWidth: openWidth || 200
}
}
]
Rectangle {
id: mainBackground
anchors.fill: parent
color: container.boxColor
radius: container.boxRadius || 10
Loader {
anchors.fill: parent
sourceComponent: container.sourceComponent
}
}
component SmoothAnim: SpringAnimation {
damping: 0.18
mass: 0.5
spring: 3
}
}

View File

@@ -1,4 +0,0 @@
import QtQuick
import Quickshell
Item {}

View File

@@ -1,290 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "Color.js" as Colors
Item {
id: workspaceRowContainer
required property HyprlandMonitor monitor
property int textBottomMargin: 0
function getNextEmptyWorkspaceId() {
let maxId = 0;
let workspaces = Hyprland.workspaces.values;
for (let i = 0; i < workspaces.length; i++) {
let ws = workspaces[i];
// Look at all numerical workspaces globally (ignoring special workspaces like scratchpads)
if (ws.id > maxId && ws.id < 99) {
maxId = ws.id;
}
}
// Returns the next global workspace ID (e.g., if max is 3, returns 4)
console.log(maxId + 1);
return maxId === 0 ? 1 : maxId + 1;
}
implicitHeight: 65
implicitWidth: 200
state: ""
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
// When not hovered, stay at default
when: !workspaceHoverHandler.hovered
PropertyChanges {
workspaceRowContainer.anchors.topMargin: -33
}
},
State {
name: "hovered"
// Keep the workspace dropped whenever hovered is true
when: workspaceHoverHandler.hovered
PropertyChanges {
workspaceRowContainer.anchors.topMargin: -20
}
PropertyChanges {
workspaceRowContainer.textBottomMargin: 7
}
}
]
Behavior on textBottomMargin {
NumberAnimation {
duration: 80
}
}
anchors {
left: parent.left
leftMargin: 15
top: parent.top
topMargin: -33
}
HoverHandler {
id: workspaceHoverHandler
}
Row {
id: workspaceRow
spacing: 5
Repeater {
model: Hyprland.workspaces.values
Item {
id: selectorContainer
property HyprlandWorkspace activeWorkspace: workspaceRowContainer.monitor.activeWorkspace
property HyprlandWorkspace currentWorkspace: Hyprland.workspaces.values[index]
property HyprlandWorkspace globalFocusedWorkspace: Hyprland.focusedWorkspace
required property int index
function getImplicitWidth() {
let implicitWidth = 25;
if (workspaceRowContainer.state == "hovered") {
implicitWidth += 5;
if (currentWorkspace.focused) {
implicitWidth += 20;
}
}
return implicitWidth;
}
function shouldBeVisible() {
let isSpecialWorkspace = !selectorContainer.currentWorkspace.name.includes("special:");
let isCurrentMonitor = Hyprland.workspaces.values[index].monitor
== workspaceRowContainer.monitor;
return isSpecialWorkspace && isCurrentMonitor;
}
implicitHeight: 50
implicitWidth: getImplicitWidth()
visible: shouldBeVisible()
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
Behavior on implicitWidth {
NumberAnimation {
duration: 100
}
}
states: [
State {
name: ""
when: !selectorHoverHandler.hovered
PropertyChanges {
selectorContainer.anchors.topMargin: 0
}
},
State {
name: "individualHovered"
when: selectorHoverHandler.hovered
PropertyChanges {
selectorContainer.anchors.topMargin: 10
}
},
State {
name: "workspaceSwitched"
PropertyChanges {
selectorContainer.anchors.topMargin: 5
}
}
]
// onActiveWorkspaceChanged: {
// if (workspaceRowContainer.monitor.activeWorkspace.id == "1") {}
// if (workspaceRowContainer.monitor.activeWorkspace.id == "4") {}
// }
onGlobalFocusedWorkspaceChanged: {
if (globalFocusedWorkspace.name === currentWorkspace.name && globalFocusedWorkspace.monitor
=== workspaceRowContainer.monitor && workspaceRowContainer.monitor.activeWorkspace.name
=== currentWorkspace.name) {
selectorContainer.state = "workspaceSwitched";
switchFeedbackTimer.restart();
}
}
Timer {
id: switchFeedbackTimer
interval: 200
repeat: false
onTriggered: selectorContainer.state = ""
}
anchors {
top: parent.top
topMargin: 0
}
HoverHandler {
id: selectorHoverHandler
}
MouseArea {
id: selectorMouseArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onClicked: selectorContainer.currentWorkspace.activate()
}
RectangularShadow {
anchors.fill: selector
blur: 20
color: Colors.crust
offset.x: 3
offset.y: 3
radius: selector.radius
spread: 7
z: -1
}
Rectangle {
id: selector
anchors.fill: parent
color: selectorContainer.currentWorkspace.focused ? Colors.mauve :
workspaceRowContainer.monitor.activeWorkspace
== selectorContainer.currentWorkspace ? Colors.lavender : Colors.overlay1
radius: 8
state: ""
z: 1
Behavior on color {
ColorAnimation {
duration: 100
}
}
Text {
id: workspaceIdentifier
font.family: "FiraMono Nerd Font"
font.pixelSize: workspaceRowContainer.state == "hovered" ? 15 : 13
text: selectorContainer.currentWorkspace.name
anchors {
bottom: parent.bottom
bottomMargin: workspaceRowContainer.textBottomMargin
horizontalCenter: parent.horizontalCenter
}
}
}
}
}
Item {
implicitHeight: 40
implicitWidth: 20
Behavior on anchors.topMargin {
NumberAnimation {
duration: 100
}
}
anchors {
top: parent.top
topMargin: workspaceRowContainer.state == "hovered" ? 0 : -10
}
MouseArea {
id: newWorkspaceButton
anchors.fill: parent
onClicked: Hyprland.dispatch(`hl.dsp.focus({workspace =
${workspaceRowContainer.getNextEmptyWorkspaceId()}})`)
}
Rectangle {
anchors.fill: parent
border.color: Colors.text
border.width: 2
color: Colors.surface2
radius: 10
Text {
color: Colors.text
font.family: "FiraMono Nerd Font"
font.pixelSize: 20
text: "+"
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
}
}
}
}
}

View File

@@ -0,0 +1,69 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Bluetooth
Singleton {
id: root
property BluetoothAdapter defaultAdapter: Bluetooth.defaultAdapter
property string defaultAdapterName: Bluetooth.defaultAdapter.adapterId
function getConnected() {
let deviceList = getDevicesList();
for (let device in deviceList) {
let currentDevice = deviceList[device];
if (currentDevice.connected == true) {
return false;
}
}
return true;
}
function getDevicesList() {
return root.defaultAdapter.devices.values;
}
function getIcon(name) {
const icons = {
"audio-card": "󰓃",
"audio-input-microphone": "",
"audio-headphones": "󰋋",
"audio-headset": "󰋋",
"battery": "󰂀",
"camera-photo": "󰻛",
"computer": "",
"input-keyboard": "󰌌",
"input-mouse": "󰍽",
"input-gaming": "󰊴",
"phone": "󰏲"
};
return icons[name] || "󰾰";
}
function pairTrustConnect(device) {
device.pair();
device.trusted = true;
device.connect();
}
function toggleDiscover() {
if (defaultAdapter.discovering == true) {
defaultAdapter.discovering = false;
} else {
defaultAdapter.discovering = true;
discoveringTimeout.running = true;
}
}
Timer {
id: discoveringTimeout
interval: 15000
repeat: false
running: false
onTriggered: defaultAdapter.discovering = false
}
}

27
Services/MprisManager.qml Normal file
View File

@@ -0,0 +1,27 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Services.Mpris
Singleton {
id: root
property MprisPlayer defaultPlayer: Mpris.players.values[0]
property real pos: defaultPlayer.position / defaultPlayer.length
function getPlaying() {
if (!defaultPlayer) {
return false;
} else {
return defaultPlayer.playbackState == MprisPlaybackState.Playing;
}
}
function prev() {
defaultPlayer.previous();
}
function skip() {
defaultPlayer.next();
}
}

View File

@@ -0,0 +1,32 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Services.Notifications
Singleton {
id: root
property alias notif: server
function getLatestNotification() {
let notificationList = server.trackedNotifications.values;
let len = notificationList.length;
if (len <= 0) {
len = 1;
}
let latestNotification = notificationList[len - 1];
return latestNotification;
}
NotificationServer {
id: server
actionsSupported: true
bodyMarkupSupported: true
bodySupported: true
onNotification: notification => {
notification.tracked = true;
}
}
}

49
Services/Time.qml Normal file
View File

@@ -0,0 +1,49 @@
// Time.qml
// with this line our type becomes a Singleton
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
// your singletons should always have Singleton as the type
Singleton {
id: root
property string date
property string time
Process {
id: timeProc
command: ["date", "+%T"]
running: true
stdout: StdioCollector {
onStreamFinished: root.time = this.text
}
}
Process {
id: dateProc
command: ["date", "+%A %B %d %Y"]
running: true
stdout: StdioCollector {
onStreamFinished: root.date = this.text
}
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: {
timeProc.running = true;
dateProc.running = true;
}
}
}

View File

@@ -0,0 +1,79 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Hyprland
Singleton {
id: root
function activateNextFreeWorkspace() {
let nextFree = getNextFreeWorkspace();
activateWorkspaceById(nextFree);
}
function activateWorkspaceById(id) {
Hyprland.dispatch(`hl.dsp.focus({workspace = ${id}})`);
}
function getAllNumberedWorkspaces() {
let allWorkspaces = Hyprland.workspaces.values;
let filteredWorkspaces = [];
for (let workspace in allWorkspaces) {
let currentWorkspace = allWorkspaces[workspace];
if (!currentWorkspace.name.includes("special")) {
if (currentWorkspace) {
filteredWorkspaces.push(currentWorkspace);
}
}
}
return filteredWorkspaces;
}
function getNextFreeWorkspace() {
let workspaceList = getAllNumberedWorkspaces();
if (workspaceList[workspaceList.length - 1].id == workspaceList.length) {
return workspaceList.length + 1;
} else {
let prevWorkspace = 0;
for (let workspace in workspaceList) {
let currentWorkspace = workspaceList[workspace];
if (currentWorkspace.id != (prevWorkspace + 1)) {
return currentWorkspace.id - 1;
} else {
prevWorkspace = currentWorkspace.id;
}
}
}
}
function getNumberOfWorkspaces(monitor) {
let workspaceList = getWorkspacesForMonitor(monitor);
return workspaceList.length;
}
function getWorkspaceState(workspace) {
if (workspace.focused == true) {
return "focused";
} else if (workspace.active == true) {
return "active";
} else {
return "inactive";
}
}
function getWorkspacesForMonitor(monitor) {
let allWorkspaces = getAllNumberedWorkspaces();
let monitorWorkspaces = [];
for (let workspace in allWorkspaces) {
let currentWorkspace = allWorkspaces[workspace];
if (currentWorkspace.monitor == monitor) {
if (currentWorkspace) {
monitorWorkspaces.push(currentWorkspace);
}
}
}
return monitorWorkspaces;
}
}

6
Services/qmldir Normal file
View File

@@ -0,0 +1,6 @@
module Services
singleton Time 1.0 Time.qml
singleton NotificationManager 1.0 NotificationManager.qml
singleton WorkspaceManager 1.0 WorkspaceManager.qml
singleton BluetoothManager 1.0 BluetoothManager.qml
singleton MprisManager 1.0 MprisManager.qml

65
devenv.lock Normal file
View File

@@ -0,0 +1,65 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1782153911,
"narHash": "sha256-Z7BG4cSJX1ybJeZztEMV8WlE6GrXJsSd34x9eHvpzU8=",
"owner": "cachix",
"repo": "devenv",
"rev": "d894e487bb23ed87e0555d2158710844bde65322",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"nixpkgs": {
"inputs": {
"nixpkgs-src": "nixpkgs-src"
},
"locked": {
"lastModified": 1782132010,
"narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-src": {
"flake": false,
"locked": {
"lastModified": 1781607440,
"narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3e41b24abd260e8f71dbe2f5737d24122f972158",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

17
devenv.nix Normal file
View File

@@ -0,0 +1,17 @@
{
pkgs,
...
}:
{
env.DEVSHELL_NAME = "󰏖 devenv/#fab387| quickshell/green";
packages = with pkgs; [
quickshell
kdePackages.qtdeclarative
];
processes = {
qs.exec = "quickshell -p .";
};
}

4
devenv.yaml Normal file
View File

@@ -0,0 +1,4 @@
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
inputs:
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling

61
flake.lock generated
View File

@@ -1,61 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1780243769,
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,42 +0,0 @@
{
description = "A simple Rust development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
utils,
}:
utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShells.default = pkgs.mkShell {
name = "flake";
buildInputs = with pkgs; [
quickshell
kdePackages.qtdeclarative
];
# Environment variables
shellHook = ''
export DEVSHELL_NAME="󱄅 flake/#89dceb| quickshell/green"
# Trigger zsh
if [[ -z "$ZSH_VERSION" ]]; then
exec zsh
fi
'';
};
}
);
}

View File

@@ -1,10 +1,7 @@
import QtQuick
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import QtQuick
import "Components/Color.js" as Colors
import "Components"
Scope { Scope {
Bar {} Bar {}
} }