This commit is contained in:
Bloxx12 2025-06-24 07:46:51 +02:00
commit 1a27b905bf
Signed by: faukah
SSH key fingerprint: SHA256:Uj2AXqvtdCA4hn5Hq0ZonhIAyUqI1q4w2sMG3Z1TH7E
16 changed files with 622 additions and 177 deletions

38
Bar.qml
View file

@ -1,38 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
PanelWindow {
id: root
color: "transparent"
anchors {
top: true
bottom: true
left: 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
wsBaseIndex: root.screen.name == "DP-2" ? 11 : 1
}
SysTray {
bar: root
}
}
}

View file

@ -1,18 +1,26 @@
import QtQuick import QtQuick
import QtQuick.Layouts
import Quickshell import Quickshell
Rectangle { Rectangle {
width: parent.width
height: text.height + 10
border.color: "black" width: text.width
implicitHeight: text.height
// border.color: "black"
border.width: 2 border.width: 2
radius: 5 radius: 0
color: "#30c0ffff" color: "transparent"
Behavior on implicitHeight {
NumberAnimation {
duration: 100
easing.type: Easing.OutCubic
}
}
Item { Item {
width: parent.width width: parent.width
height: text.height * 2 height: text.height
anchors.centerIn: parent anchors.centerIn: parent
@ -26,11 +34,11 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
property var date: Date() property var date: Date()
text: Qt.formatDateTime(clock.date, "hh\nmm") text: Qt.formatDateTime(clock.date, "hh mm")
font.family: "ComicShannsMono Nerd Font Mono" font.family: "ComicShannsMono Nerd Font Mono"
font.weight: Font.ExtraBold font.weight: Font.ExtraBold
font.pointSize: 18 font.pointSize: 12
color: "black" color: "black"
} }

View file

@ -18,7 +18,7 @@ Rectangle {
border.color: "black" border.color: "black"
border.width: 2 border.width: 2
ColumnLayout { RowLayout {
id: column id: column
spacing: 10 spacing: 10

View file

@ -1,116 +0,0 @@
// 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 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: "#30c0ffff"
border.width: 2
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: wsItem.height - 5
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);
});
}
}

46
config/Config.qml Normal file
View file

@ -0,0 +1,46 @@
pragma Singleton
import Quickshell
import QtQuick
Singleton {
id: root
readonly property QtObject bar: QtObject {
readonly property int width: 50
readonly property var colors: QtObject {
readonly property color bar: "#1e1e2e"
readonly property color barOutline: "#50ffffff"
readonly property color widget: "#25ceffff"
readonly property color widgetActive: "#80ceffff"
readonly property color widgetOutline: "#40ffffff"
readonly property color widgetOutlineSeparate: "#20ffffff"
readonly property color separator: "#60ffffff"
}
}
readonly property QtObject border: QtObject {
readonly property int thickness: 8
readonly property color color: "#1e1e2e"
readonly property int rounding: 25
}
readonly property QtObject catppuccin: QtObject {
readonly property color base: "#1e1e2e"
readonly property color mantle: "#181825"
readonly property color surface0: "#313244"
readonly property color surface1: "#45475a"
readonly property color surface2: "#585b70"
readonly property color text: "#cdd6f4"
readonly property color rosewater: "#f5e0dc"
readonly property color lavender: "#b4befe"
readonly property color red: "#f38ba8"
readonly property color peach: "#fab387"
readonly property color yellow: "#f9e2af"
readonly property color green: "#a6e3a1"
readonly property color teal: "#a6e3a1"
readonly property color blue: "#89b4fa"
readonly property color mauve: "#cba6f7"
readonly property color flamingo: "#f2cdcd"
}
}

57
modules/bar/Bar.qml Normal file
View file

@ -0,0 +1,57 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import "../../config"
import "components"
Rectangle {
id: root
color: Config.bar.colors.bar
required property ShellScreen screen
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
}
implicitWidth: Config.bar.width
Item {
id: child
anchors {
top: parent.top
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
implicitWidth: Math.max(clock.implicitWidth, workspaces.implicitWidth)
ColumnLayout {
spacing: 2
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 0
}
Clock {
id: clock
}
Workspaces {
id: workspaces
screen: root.screen
}
}
Text {
text: root.screen.name
color: "green"
}
}
}

View file

@ -0,0 +1,34 @@
import QtQuick
import Quickshell
import "../../../config"
Rectangle {
id: root
width: text.width + 5
height: text.height + 5
implicitWidth: width
border.color: Config.catppuccin.rosewater
border.width: 0
radius: 5
color: "transparent"
Text {
id: text
anchors.centerIn: parent
property var date: Date()
text: Qt.formatDateTime(clock.date, "hh mm")
font.family: "JetBrainsMono NF Mono"
font.pointSize: 15
color: Config.catppuccin.text
}
SystemClock {
id: clock
precision: SystemClock.Seconds
}
}

View file

@ -0,0 +1,103 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import QtQuick.Layouts
import "../../../services/niri"
import "../../../config"
Rectangle {
id: root
required property ShellScreen screen
property var workspaces: Niri.workspaces
property var activeWorkspace: Niri.activeWorkspace
property var activeWorkspaceIndex: Niri.activeWorkspaceIndex
property int wsItemHeight: 15
property bool _: log()
function log() {
console.debug("Screen name: " + screen.name);
console.debug("Found the following workspaces:");
for (let i = 0; i < workspaces.length; i++) {
console.debug("Workspace " + workspaces[i].id + " On screen " + workspaces[i].output + " With name: " + workspaces[i].name);
// console.debug(workspaces[i].output);
}
return true;
}
// Works
height: 300
// Gives warning
// height: workspaces.length * root.wsItemHeight
implicitWidth: list.implicitWidth
color: "transparent"
border.color: Config.catppuccin.rosewater
border.width: 0
radius: 7
Layout.fillWidth: true
ListView {
id: list
model: root.workspaces
implicitHeight: contentHeight
implicitWidth: contentItem.childrenRect.width
anchors.horizontalCenter: parent.horizontalCenter
// anchors.fill: parent
delegate: Item {
id: wsItem
// Name of the workspace
property string name: "VOID"
// ID of the workspace
required property string id
required property string output
property bool isActive: (id - 1 == root.activeWorkspaceIndex)
property real animActive: isActive ? 1 : 0.65
Behavior on animActive {
NumberAnimation {
duration: 150
}
}
// property bool isCorrectScreen: log()
// function log() {
// console.log("Screen name: " + root.screen.name);
// console.log(wsItem.output);
// console.log(wsItem.id);
// let isCorrect = root.screen.name == wsItem.output;
// console.log("isCorrect: ", isCorrect);
// return root.screen.name == wsItem.output;
// }
implicitHeight: root.wsItemHeight
implicitWidth: 50
anchors {
right: parent.right
left: parent.left
}
Rectangle {
anchors.centerIn: parent
height: wsItem.height - 5
width: parent.width * wsItem.animActive
radius: height / 2
border.color: Config.catppuccin.mantle
border.width: 0
color: Config.catppuccin.blue
// visible: wsItem.isCorrectScreen
}
}
}
}

View file

@ -0,0 +1,3 @@
import QtQuick
import QtQuick.Shapes

View file

@ -0,0 +1,54 @@
import Quickshell
import QtQuick
import QtQuick.Effects
import "../../config"
Item {
id: root
required property Rectangle bar
anchors.fill: parent
Rectangle {
id: rect
anchors.fill: parent
color: Config.border.color
visible: false
Behavior on color {
ColorAnimation {
duration: 150
easing.type: Easing.BezierSpline
}
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Config.border.thickness
anchors.leftMargin: root.bar.implicitWidth
radius: Config.border.rounding
}
}
MultiEffect {
anchors.fill: parent
maskEnabled: true
maskInverted: true
maskSource: mask
source: rect
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}

117
modules/drawers/Drawers.qml Normal file
View file

@ -0,0 +1,117 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Notifications
import QtQuick
import QtQuick.Effects
import "../bar"
import "../notifications"
// import "../../services"
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
Exclusions {
screen: scope.modelData
bar: bar
}
PanelWindow {
id: win
screen: scope.modelData
color: "transparent"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
// Clickthrough mask.
// Clickable areas of the window are determined by the provided region.
mask: Region {
x: bar.implicitWidth
y: 8
width: win.width - bar.implicitWidth
height: win.height - 8
// Setting the intersection mode to Xor will invert the mask and make everything in the mask region not clickable and pass through clicks inside it through the window.
intersection: Intersection.Xor
}
anchors {
top: true
bottom: true
left: true
right: true
}
Item {
id: background
anchors.fill: parent
visible: false
Border {
bar: bar
}
}
MultiEffect {
anchors.fill: source
source: background
shadowEnabled: true
blurMax: 15
}
Bar {
id: bar
screen: scope.modelData
}
Item {
id: notifs
readonly property list<Notif> list: []
readonly property list<Notif> popups: list.filter(n => n.popup)
NotificationServer {
id: server
keepOnReload: false
onNotification: notif => {
notif.tracked = true;
console.log("Got notification: " + notif.body);
root.list.push(notifComp.createObject(root, {
popup: true,
notification: notif,
body: notif.body,
appName: notif.appName
}));
}
}
Component {
id: notifComp
Notif {}
}
}
}
}
component Notif: QtObject {
property bool popup
readonly property date time: new Date()
required property Notification notification
readonly property string body: notification.body
readonly property string appName: notification.appName
}
}

View file

@ -0,0 +1,38 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import "../../config"
Scope {
id: root
required property ShellScreen screen
required property Item bar
ExclusionZone {
anchors.left: true
exclusiveZone: root.bar.implicitWidth
}
ExclusionZone {
anchors.top: true
}
ExclusionZone {
anchors.right: true
}
ExclusionZone {
anchors.bottom: true
}
component ExclusionZone: PanelWindow {
screen: root.screen
color: "transparent"
exclusiveZone: Config.border.thickness
implicitHeight: Config.border.thickness
implicitWidth: Config.border.thickness
mask: Region {}
}
}

View file

@ -0,0 +1,18 @@
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Notifications
import QtQuick
import QtQuick.Layouts
import "../../config"
import "../../services"
Rectangle {
id: root
color: "transparent"
required property Notification.Notif modelData
Text {
text: root.modelData.summary
}
}

42
services/Notification.qml Normal file
View file

@ -0,0 +1,42 @@
pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
Singleton {
id: root
readonly property list<Notif> list: []
readonly property list<Notif> popups: list.filter(n => n.popup)
NotificationServer {
id: server
keepOnReload: false
onNotification: notif => {
notif.tracked = true;
root.list.push(notifComp.createObject(root, {
popup: true,
notification: notif
}));
}
}
component Notif: QtObject {
property bool popup
readonly property date time: new Date()
required property Notification notification
readonly property string summary: notification.summary
}
Component {
id: notifComp
Notif {}
}
}

85
services/niri/Niri.qml Normal file
View file

@ -0,0 +1,85 @@
// Kind thanks to https://github.com/MapoMagpie/nixos-flakes/blob/main/home/ui/quickshell/config/Data/Niri.qml
// This file was taken from there and further modified.
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
// property var data
property var workspaces: []
property var activeWorkspace: "VOID"
property var activeWorkspaceIndex: 0
property var windows: []
// property var activedWindowId: 0
Process {
id: proc
command: ["niri", "msg", "-j", "event-stream"]
running: true
stdout: SplitParser {
onRead: data => {
var event = JSON.parse(data);
let workspaces = [];
if (event.WorkspacesChanged) {
root.workspaces = event.WorkspacesChanged.workspaces.filter(w => w.name);
root.workspaces = root.workspaces.sort((a, b) => a.id - b.id);
root.activeWorkspaceIndex = root.workspaces.findIndex(w => w.is_focused);
if (root.activeWorkspaceIndex < 0) {
root.activeWorkspaceIndex = 0;
}
root.activeWorkspace = root.workspaces[root.activeWorkspaceIndex].name;
}
if (event.WindowsChanged) {
root.windows = [...event.WindowsChanged.windows].sort((a, b) => a.id - b.id);
}
if (event.WindowOpenedOrChanged) {
const window = event.WindowOpenedOrChanged.window;
const index = root.windows.findIndex(w => w.id === window.id);
// console.log("window opened or changed: ", index, ", win id: ", window.id);
if (index >= 0) {
// console.log("replace window, old: ", root.windows[index].id, ", new: ", window.id);
root.windows[index] = window;
} else {
// console.log("push window, new: ", window.id);
root.windows.push(window);
}
root.windows = [...root.windows.sort((a, b) => a.id - b.id)];
}
if (event.WindowClosed) {
const index = root.windows.findIndex(w => w.id === event.WindowClosed.id);
// console.log("window closed: ", index, ", win id: ", event.WindowClosed.id);
if (index >= 0) {
root.windows.splice(index, 1);
}
root.windows = [...root.windows.sort((a, b) => a.id - b.id)];
}
if (event.WorkspaceActivated) {
root.activeWorkspaceIndex = root.workspaces.findIndex(w => w.id === event.WorkspaceActivated.id);
if (root.activeWorkspaceIndex < 0) {
root.activeWorkspaceIndex = 0;
}
root.activeWorkspace = root.workspaces[root.activeWorkspaceIndex].name;
}
}
}
}
}
// {
// "workspaces": [
// {
// "id": 5,
// "idx": 4,
// "name": "GAME",
// "output": "DP-3",
// "is_active": false,
// "is_focused": false,
// "active_window_id": null
// },
// ]
// }

View file

@ -1,24 +1,18 @@
pragma ComponentBehavior: Bound //@ pragma Env QS_NO_RELOAD_POPUP=1
import Quickshell import Quickshell
import QtQuick import QtQuick
import "modules"
import "modules/drawers"
// import "modules/background"
ShellRoot { ShellRoot {
id: shellroot id: shellroot
Component.onCompleted: [Launcher.init()] Component.onCompleted: [Launcher.init()]
ReloadPopup {} Drawers {}
// Background {}
Variants {
model: Quickshell.screens
Scope {
property var modelData
Bar {
screen: modelData
}
}
}
} }