removed eww and waybar added quickshell

This commit is contained in:
samantha42
2026-05-18 08:19:40 +02:00
parent f7d6f07e3a
commit 36ee6f35da
36 changed files with 713 additions and 1657 deletions

View File

@@ -0,0 +1,29 @@
import QtQuick
// Mirrors eww clock-widget: click to toggle time ↔ date
Rectangle {
id: root
property string clockTime: ""
property string clockDate: ""
property bool showDate: false
implicitWidth: lbl.implicitWidth + 16
implicitHeight: 22
radius: 4
color: "#313244"
Text {
id: lbl
anchors.centerIn: parent
text: root.showDate ? root.clockDate : root.clockTime
color: "#cdd6f4"
font.pixelSize: 12
font.family: "monospace"
}
MouseArea {
anchors.fill: parent
onClicked: root.showDate = !root.showDate
}
}

8
quickshell/CpuWidget.qml Normal file
View File

@@ -0,0 +1,8 @@
import QtQuick
import Quickshell.Io
// eww cpu-widget
StatPill {
property string cpuUsage: ""
text: cpuUsage + "%"
}

71
quickshell/LogoutMenu.qml Normal file
View File

@@ -0,0 +1,71 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Io
// Mirrors eww logout-menu
Item {
id: root
property bool open: false
implicitHeight: 26
implicitWidth: lrow.implicitWidth
Row {
id: lrow
spacing: 4
layoutDirection: Qt.RightToLeft
// Toggle btn
Rectangle {
width: 26; height: 26; radius: 13
color: root.open ? "#89b4fa" : "#313244"
Text {
anchors.centerIn: parent
text: root.open ? "󰅖" : "󰍃"
color: root.open ? "#1e1e2e" : "#cdd6f4"
font.pixelSize: 14
}
MouseArea {
anchors.fill: parent
onClicked: root.open = !root.open
cursorShape: Qt.PointingHandCursor
}
}
// Revealed actions
Item {
id: menuReveal
width: root.open ? actionRow.implicitWidth + 8 : 0
height: 26
clip: true
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
Row {
id: actionRow
anchors.verticalCenter: parent.verticalCenter
spacing: 4
x: 4
// Lock
PowerBtn { icon: "󰌾"; onActivated: run(["hyprlock"]) }
// Reboot
PowerBtn { icon: "󰜉"; onActivated: run(["systemctl","reboot"]) }
// Power off
PowerBtn { icon: "󰐥"; onActivated: run(["systemctl","poweroff"]) }
// Logout (exit Hyprland)
PowerBtn { icon: "󰍃"; onActivated: run(["hyprctl","dispatch","exit"]) }
}
}
}
function run(cmd) {
root.open = false
Qt.createQmlObject(
'import Quickshell.Io; Process { command: ' +
JSON.stringify(cmd) + '; running: true }',
root)
}
}

7
quickshell/MemWidget.qml Normal file
View File

@@ -0,0 +1,7 @@
import QtQuick
// eww mem-widget
StatPill {
property string memUsed: ""
text: memUsed + "G"
}

View File

@@ -0,0 +1,12 @@
import QtQuick
// eww network-widget
StatPill {
property string networkInfo: ""
text: {
if (networkInfo === "offline") return "Offline"
if (networkInfo.startsWith("wifi:")) return networkInfo
return networkInfo
}
}

28
quickshell/PowerBtn.qml Normal file
View File

@@ -0,0 +1,28 @@
import QtQuick
// Reusable icon button for LogoutMenu actions (eww center-btn)
Rectangle {
id: root
property string icon: ""
signal activated()
width: 26; height: 26; radius: 13
color: ma.containsMouse ? "#f38ba8" : "#313244"
Behavior on color { ColorAnimation { duration: 120 } }
Text {
anchors.centerIn: parent
text: root.icon
color: "#cdd6f4"
font.pixelSize: 13
}
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
onClicked: root.activated()
cursorShape: Qt.PointingHandCursor
}
}

View File

@@ -0,0 +1,13 @@
import QtQuick
import Quickshell.Io
// Simple settings icon button used on HDMI bar (eww settings-btn)
StatPill {
text: "󰒓"
clickable: true
onClicked: {
Qt.createQmlObject(
'import Quickshell.Io; Process { command: ["your-settings-command"]; running: true }',
parent)
}
}

100
quickshell/SettingsMenu.qml Normal file
View File

@@ -0,0 +1,100 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Io
// Mirrors eww settings-menu (revealer + toggle button)
Item {
id: root
property string volumeInfo: ""
property string networkInfo: ""
property string themeInfo: ""
// exposed so LogoutMenu can close us
property bool open: false
implicitHeight: 26
implicitWidth: row.implicitWidth
Row {
id: row
spacing: 4
layoutDirection: Qt.RightToLeft // toggle btn on the right, menu slides left
// Toggle button (eww center-btn)
Rectangle {
width: 26; height: 26; radius: 13
color: root.open ? "#89b4fa" : "#313244"
Text {
anchors.centerIn: parent
text: root.open ? "󰅖" : "󰒓"
color: root.open ? "#1e1e2e" : "#cdd6f4"
font.pixelSize: 14
}
MouseArea {
anchors.fill: parent
onClicked: root.open = !root.open
cursorShape: Qt.PointingHandCursor
}
}
// Revealed menu (slides in from right)
Item {
width: menuRow.implicitWidth + 8
height: 26
clip: true
// animate width for slide effect (like :transition slideright)
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
// Collapsed when closed
states: State {
name: "hidden"
when: !root.open
PropertyChanges { target: menuReveal; width: 0 }
}
Item {
id: menuReveal
width: root.open ? menuRow.implicitWidth + 8 : 0
height: 26
clip: true
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
Row {
id: menuRow
anchors.verticalCenter: parent.verticalCenter
spacing: 4
x: 4
ThemeWidget { themeInfo: root.themeInfo }
VolumeWidget { volumeInfo: root.volumeInfo }
NetworkWidget { networkInfo: root.networkInfo }
// wdisplays button
Rectangle {
width: 30; height: 22; radius: 11
color: "#313244"
Text {
anchors.centerIn: parent
text: "󰍹"
color: "#cdd6f4"
font.pixelSize: 13
}
MouseArea {
anchors.fill: parent
onClicked: {
root.open = false
Qt.createQmlObject(
'import Quickshell.Io; Process { command: ["wdisplays"]; running: true }',
root)
}
cursorShape: Qt.PointingHandCursor
}
}
}
}
}
}
}

34
quickshell/StatPill.qml Normal file
View File

@@ -0,0 +1,34 @@
import QtQuick
// Generic stat pill matches eww stat-pill
Rectangle {
id: root
property string text: ""
property bool clickable: false
signal clicked()
signal rightClicked()
implicitWidth: lbl.implicitWidth + 16
implicitHeight: 22
radius: 11 // fully rounded pill
color: "#313244"
Text {
id: lbl
anchors.centerIn: parent
text: root.text
color: "#cdd6f4"
font.pixelSize: 12
font.family: "monospace"
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
if (mouse.button === Qt.RightButton) root.rightClicked()
else root.clicked()
}
cursorShape: root.clickable ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}

View File

@@ -0,0 +1,19 @@
import QtQuick
import Quickshell.Io
// eww theme-widget
StatPill {
property string themeInfo: ""
text: themeInfo
clickable: true
onClicked: launchTheme("next")
onRightClicked: launchTheme("auto")
function launchTheme(mode) {
Qt.createQmlObject(
'import Quickshell.Io; Process { command: ["bash","' +
Qt.resolvedUrl("../hypr/theme-cycle.sh") +
'","' + mode + '"]; running: true }', parent)
}
}

View File

@@ -0,0 +1,50 @@
import QtQuick
import Quickshell.Io
// eww volume-widget
Rectangle {
id: root
property string volumeInfo: ""
readonly property bool muted: volumeInfo.startsWith("muted")
readonly property int level: parseInt(volumeInfo) || 0
readonly property string icon:
muted ? "󰝟" :
level < 34 ? "󰕿" :
level < 67 ? "󰖀" : "󰕾"
implicitWidth: row.implicitWidth + 16
implicitHeight: 22
radius: 11
color: "#313244"
Row {
id: row
anchors.centerIn: parent
spacing: 4
Text {
text: root.icon
color: "#cdd6f4"
font.pixelSize: 13
}
Text {
text: root.muted ? "Muted" : root.level + "%"
color: "#cdd6f4"
font.pixelSize: 12
font.family: "monospace"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.createQmlObject(
'import Quickshell.Io; Process { command: ["bash","' +
Qt.resolvedUrl("scripts/toggle-mixer.sh") +
'"]; running: true }', root)
}
cursorShape: Qt.PointingHandCursor
}
}

View File

@@ -0,0 +1,65 @@
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
import QtQuick.Layouts
// Mirrors eww workspaces-widget / workspace-btn
Item {
id: root
property var workspaces: []
implicitHeight: row.implicitHeight
implicitWidth: row.implicitWidth
RowLayout {
id: row
spacing: 10
Repeater {
model: root.workspaces
// workspace-btn equivalent
Rectangle {
id: wsBtn
required property var modelData
readonly property bool active: modelData.active ?? false
readonly property bool urgent: modelData.urgent ?? false
readonly property bool empty: modelData.empty ?? false
implicitWidth: Math.max(label.implicitWidth + 16,
modelData.pxwidth ?? 28)
implicitHeight: 22
radius: 4
color: active ? "#89b4fa" // blue active
: urgent ? "#f38ba8" // red urgent
: empty ? "transparent" // ghost empty
: "#313244" // normal
border.color: empty && !active ? "#585b70" : "transparent"
border.width: empty && !active ? 1 : 0
Text {
id: label
anchors.centerIn: parent
text: wsBtn.modelData.label ?? ""
color: wsBtn.active ? "#1e1e2e" : "#cdd6f4"
font.pixelSize: 12
font.family: "monospace"
}
MouseArea {
anchors.fill: parent
onClicked: {
var proc = Qt.createQmlObject(
'import Quickshell.Io; Process { command: ["hyprctl","dispatch","workspace","' +
wsBtn.modelData.id + '"]; running: true }',
wsBtn)
}
}
}
}
}
}

247
quickshell/shell.qml Normal file
View File

@@ -0,0 +1,247 @@
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import QtQuick.Layouts
// ─────────────────────────────────────────────
// Shell root spawns one bar per screen
// ─────────────────────────────────────────────
ShellRoot {
id: root
// ── Global polled data ───────────────────
property string clockTime: ""
property string clockDate: ""
property string cpuUsage: ""
property string memUsed: ""
property string networkInfo: ""
property string volumeInfo: ""
property string themeInfo: ""
// Clock every second
Process {
id: clockTimeProc
command: ["date", "+%H:%M:%S"]
running: true
stdout: StdioCollector { onStreamFinished: clockTime = this.text.trim() }
}
Timer {
interval: 1000; running: true; repeat: true
onTriggered: clockTimeProc.running = true
}
Process {
id: clockDateProc
command: ["date", "+%A, %B %d %Y"]
running: true
stdout: StdioCollector { onStreamFinished: clockDate = this.text.trim() }
}
Timer {
interval: 1000; running: true; repeat: true
onTriggered: clockDateProc.running = true
}
// CPU every 2 s
Process {
id: cpuProc
command: ["bash", "-c", "top -bn1 | grep 'Cpu(s)' | awk '{print int($2+$4)}'"]
running: true
stdout: StdioCollector { onStreamFinished: cpuUsage = this.text.trim() }
}
Timer {
interval: 2000; running: true; repeat: true
onTriggered: cpuProc.running = true
}
// Memory every 2 s
Process {
id: memProc
command: ["bash", "-c", "free -g --si | awk '/Mem:/{printf \"%.1f\", $3}'"]
running: true
stdout: StdioCollector { onStreamFinished: memUsed = this.text.trim() }
}
Timer {
interval: 2000; running: true; repeat: true
onTriggered: memProc.running = true
}
// Network every 5 s
Process {
id: netProc
command: ["bash", Qt.resolvedUrl("scripts/get-network.sh")]
running: true
stdout: StdioCollector { onStreamFinished: networkInfo = this.text.trim() }
}
Timer {
interval: 5000; running: true; repeat: true
onTriggered: netProc.running = true
}
// Volume every 1 s
Process {
id: volProc
command: ["bash", Qt.resolvedUrl("scripts/get-volume.sh")]
running: true
stdout: StdioCollector { onStreamFinished: volumeInfo = this.text.trim() }
}
Timer {
interval: 1000; running: true; repeat: true
onTriggered: volProc.running = true
}
// Theme every 2 s
Process {
id: themeProc
command: ["bash", Qt.resolvedUrl("scripts/get-theme.sh")]
running: true
stdout: StdioCollector { onStreamFinished: themeInfo = this.text.trim() }
}
Timer {
interval: 2000; running: true; repeat: true
onTriggered: themeProc.running = true
}
// ── One bar per screen ───────────────────
Variants {
model: Quickshell.screens
PanelWindow {
id: bar
required property var modelData
screen: modelData
color: '#1c1d20'
anchors { top: true; left: true; right: true }
implicitHeight: 30
exclusiveZone: implicitHeight // reserve space (replaces :exclusive true)
// Which screen slot is this? (0 = primary → ws 15, 1 = secondary → ws 610)
property int screenIndex: {
var screens = Quickshell.screens
for (var i = 0; i < screens.length; i++) {
if (screens[i] === modelData) return i
}
return 0
}
// The 5 workspace IDs that belong to this bar
readonly property int wsFirst: screenIndex === 0 ? 1 : 6
readonly property int wsLast: screenIndex === 0 ? 5 : 10
// ── Bar layout (centerbox equivalent) ──
RowLayout {
anchors.fill: parent
spacing: 0
anchors.leftMargin: 8
anchors.rightMargin: 8
// LEFT workspaces (native Hyprland model, filtered per screen)
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Row {
anchors { left: parent.left; verticalCenter: parent.verticalCenter }
spacing: 4
Repeater {
// Always show slots wsFirst..wsLast, even if Hyprland
// hasn't created the workspace yet (shows as empty).
model: bar.wsLast - bar.wsFirst + 1
delegate: Rectangle {
required property int index
readonly property int wsId: bar.wsFirst + index
// Look up live workspace from Hyprland
readonly property HyprlandWorkspace liveWs: {
for (var i = 0; i < Hyprland.workspaces.values.length; i++) {
if (Hyprland.workspaces.values[i].id === wsId)
return Hyprland.workspaces.values[i]
}
return null
}
readonly property bool active: Hyprland.focusedMonitor !== null
&& Hyprland.focusedMonitor.activeWorkspace !== null
&& Hyprland.focusedMonitor.activeWorkspace.id === wsId
&& Hyprland.focusedMonitor.name === bar.modelData.name
readonly property bool occupied: liveWs !== null
&& liveWs.windowCount > 0
implicitWidth: 22
implicitHeight: 22
radius: 4
color: active ? "#89b4fa" // focused
: occupied ? "#313244" // has windows
: "transparent" // empty
border.color: active ? "transparent" : "#585b70"
border.width: active ? 0 : 1
Text {
anchors.centerIn: parent
text: wsId
color: active ? "#1e1e2e" : "#cdd6f4"
font.pixelSize: 11
font.family: "monospace"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Hyprland.dispatch("workspace " + wsId)
}
}
}
}
}
// CENTER clock
Item {
Layout.fillWidth: true
Layout.fillHeight: true
ClockWidget {
anchors.centerIn: parent
clockTime: root.clockTime
clockDate: root.clockDate
}
}
// RIGHT stats + menus
Item {
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
spacing: 2
// Primary screen (0) gets full menus; secondary gets stats only
SettingsMenu {
visible: bar.screenIndex === 0
volumeInfo: root.volumeInfo
networkInfo: root.networkInfo
themeInfo: root.themeInfo
}
LogoutMenu {
visible: bar.screenIndex === 0
}
CpuWidget { cpuUsage: root.cpuUsage }
MemWidget { memUsed: root.memUsed }
}
}
}
}
}
}