aboutsummaryrefslogtreecommitdiff
path: root/quickshell
diff options
context:
space:
mode:
authorAargh Rai <aargh.rai+git@gmail.com>2025-12-08 16:58:52 +0530
committerAargh Rai <aargh.rai+git@gmail.com>2025-12-08 16:58:52 +0530
commitaf844bbc44741a971614175248cc29505d1acbd1 (patch)
treed75c8dc30e7f0f0596b5f087ad3697678a042e3e /quickshell
parent15bed9293ad7fb0b54cef68cdbf1a0549ec7aa97 (diff)
replaced wofi with custom app menu + right side apps bar + organization of qml files
Diffstat (limited to 'quickshell')
-rw-r--r--quickshell/AppLauncher.qml248
-rw-r--r--quickshell/IconButton.qml41
-rw-r--r--quickshell/Procs.qml111
-rw-r--r--quickshell/items/DataBus.qml23
-rw-r--r--quickshell/items/Electrons.qml23
-rw-r--r--quickshell/items/Latch.qml23
-rw-r--r--quickshell/items/Photons.qml32
-rw-r--r--quickshell/items/Radiation.qml23
-rw-r--r--quickshell/items/Radio.qml28
-rw-r--r--quickshell/items/Spin.qml45
-rw-r--r--quickshell/items/Waves.qml23
-rw-r--r--quickshell/items/Workspace.qml22
-rwxr-xr-xquickshell/scripts/battery.sh (renamed from quickshell/battery.sh)0
-rwxr-xr-xquickshell/scripts/brightness.sh (renamed from quickshell/brightness.sh)0
-rwxr-xr-xquickshell/scripts/cpu_temp.sh (renamed from quickshell/cpu_temp.sh)0
-rwxr-xr-xquickshell/scripts/list-apps.sh115
-rwxr-xr-xquickshell/scripts/network.sh (renamed from quickshell/network.sh)0
-rwxr-xr-xquickshell/scripts/volume.sh (renamed from quickshell/volume.sh)0
-rw-r--r--quickshell/shell.qml509
19 files changed, 925 insertions, 341 deletions
diff --git a/quickshell/AppLauncher.qml b/quickshell/AppLauncher.qml
new file mode 100644
index 0000000..a1b5ab2
--- /dev/null
+++ b/quickshell/AppLauncher.qml
@@ -0,0 +1,248 @@
+import QtQuick
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Io
+
+Rectangle {
+ id: appLauncher
+
+ width: 400
+ height: 600
+ color: shellRoot.black
+ radius: 16
+ border.width: 3
+ border.color: shellRoot.blue
+ antialiasing: true
+
+ property bool isVisible: false
+ property int selectedIndex: -1
+ property int hoverIndex: -1
+
+ signal requestClose()
+
+ focus: true
+
+ Keys.onEscapePressed: requestClose()
+
+ Keys.onUpPressed: {
+ hoverIndex = -1
+ if (selectedIndex === -1) {
+ selectedIndex = appListModel.count - 1
+ } else if (selectedIndex > 0) {
+ selectedIndex--
+ }
+ listView.positionViewAtIndex(selectedIndex, ListView.Contain)
+ }
+
+ Keys.onDownPressed: {
+ hoverIndex = -1
+ if (selectedIndex === -1) {
+ selectedIndex = 0
+ } else if (selectedIndex < appListModel.count - 1) {
+ selectedIndex++
+ }
+ listView.positionViewAtIndex(selectedIndex, ListView.Contain)
+ }
+
+ Keys.onReturnPressed: {
+ if (selectedIndex >= 0 && selectedIndex < appListModel.count) {
+ launchApp(appListModel.get(selectedIndex).appCommand)
+ }
+ }
+
+ Keys.onEnterPressed: {
+ if (selectedIndex >= 0 && selectedIndex < appListModel.count) {
+ launchApp(appListModel.get(selectedIndex).appCommand)
+ }
+ }
+
+ // Reset selection when widget becomes visible
+ onIsVisibleChanged: {
+ if (isVisible) {
+ selectedIndex = -1 // Start with nothing selected
+ hoverIndex = -1 // Reset hover on open
+ appLauncher.forceActiveFocus()
+ // Reload apps every time the launcher opens
+ loadApps()
+ }
+ }
+
+ // Process to load apps
+ Process {
+ id: appLoader
+ running: false // Don't auto-start, we'll trigger it manually
+ command: ["sh", "-c", "~/.config/quickshell/scripts/list-apps.sh"]
+
+ stdout: SplitParser {
+ onRead: data => {
+ const lines = data.split('\n')
+ for (const line of lines) {
+ if (line.trim().length === 0) continue
+
+ const parts = line.split('|')
+ if (parts.length >= 4) {
+ appListModel.append({
+ appName: parts[0],
+ appDescription: parts[1],
+ appIcon: parts[2],
+ appCommand: parts[3]
+ })
+ }
+ }
+ console.log("Loaded", appListModel.count, "applications")
+ }
+ }
+
+ onRunningChanged: {
+ // When process finishes, reset it for next run
+ if (!running) {
+ appLoader.running = false
+ }
+ }
+ }
+
+ // Function to load/reload applications
+ function loadApps() {
+ // Clear existing apps
+ appListModel.clear()
+ // Start the process to reload
+ appLoader.running = true
+ }
+
+ // Load apps initially when component is created
+ Component.onCompleted: {
+ loadApps()
+ }
+
+ Column {
+ anchors.fill: parent
+ anchors.margins: 12
+ spacing: 8
+
+ // Header
+ Rectangle {
+ width: parent.width
+ height: 36
+ color: "transparent"
+
+ Text {
+ anchors.centerIn: parent
+ text: "Applications"
+ font.family: shellRoot.fontFamily
+ font.pixelSize: 14
+ font.weight: Font.Medium
+ color: shellRoot.white
+ }
+
+ // Close button
+ Rectangle {
+ width: 28
+ height: 28
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ radius: 6
+ color: closeMouseArea.containsMouse ? shellRoot.red : "transparent"
+
+ Text {
+ anchors.centerIn: parent
+ text: "✕"
+ font.family: shellRoot.fontFamily
+ font.pixelSize: 16
+ font.weight: Font.Bold
+ color: closeMouseArea.containsMouse ? shellRoot.red : shellRoot.orange
+ }
+
+ MouseArea {
+ id: closeMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: appLauncher.requestClose()
+ }
+ }
+ }
+
+ // Apps list
+ ListView {
+ id: listView
+ width: parent.width
+ height: parent.height - 44
+ spacing: 8
+ clip: true
+
+ model: ListModel {
+ id: appListModel
+ // Apps will be loaded dynamically from the script
+ }
+
+ delegate: Rectangle {
+ width: listView.width
+ height: 30
+ color: {
+ if (appLauncher.hoverIndex === index) return shellRoot.lightgray
+ if (appLauncher.hoverIndex === -1 && index === shellRoot.selectedIndex) return shellRoot.lightgray
+ return shellRoot.gray
+ }
+ radius: 12
+ border.width: 2
+ border.color: {
+ if (appLauncher.hoverIndex === index || (shellRoot.hoverIndex === -1 && index === shellRoot.selectedIndex)) return shellRoot.blue
+ return "transparent"
+ }
+
+ Row {
+ anchors.fill: parent
+ anchors.leftMargin: 12
+ anchors.rightMargin: 12
+ anchors.topMargin: 8
+ anchors.bottomMargin: 8
+ spacing: 12
+
+ Text {
+ width: parent.width - 62
+ anchors.centerIn: parent
+ text: model.appName
+ font.family: shellRoot.fontFamily
+ font.pixelSize: 14
+ font.weight: Font.Medium
+ color: {
+ if (appLauncher.hoverIndex === index) return shellRoot.blue
+ if (appLauncher.hoverIndex === -1 && index === shellRoot.selectedIndex) return shellRoot.blue
+ return shellRoot.purple
+ }
+ elide: Text.ElideRight
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+
+ onEntered: {
+ appLauncher.hoverIndex = index
+ }
+ onExited: {
+ appLauncher.hoverIndex = -1
+ }
+
+ onClicked: {
+ launchApp(model.appCommand)
+ }
+ }
+ }
+ }
+ }
+
+ function launchApp(command) {
+ console.log("Launching app:", command)
+ // Close the launcher first
+ appLauncher.requestClose()
+
+ // Use a small delay before launching to ensure the window closes properly
+ Qt.callLater(() => {
+ Quickshell.execDetached(["sh", "-c", command + " &"])
+ })
+ }
+}
diff --git a/quickshell/IconButton.qml b/quickshell/IconButton.qml
new file mode 100644
index 0000000..96c4c7f
--- /dev/null
+++ b/quickshell/IconButton.qml
@@ -0,0 +1,41 @@
+import QtQuick
+
+MouseArea {
+ id: iconButton
+
+ property string icon: ""
+ property string tooltip: ""
+
+ width: 32
+ height: 32
+
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ enabled: true
+ z: 10
+
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ radius: 6
+
+ Behavior on color {
+ ColorAnimation { duration: 200 }
+ }
+
+ Text {
+ id: iconText
+ anchors.centerIn: parent
+ text: iconButton.icon
+ font.family: shellRoot.fontFamily
+ font.pixelSize: 22
+ color: shellRoot.white
+
+ Component.onCompleted: {
+ console.log("IconButton text:", iconButton.icon, "length:", iconButton.icon.length)
+ }
+ }
+ }
+
+
+}
diff --git a/quickshell/Procs.qml b/quickshell/Procs.qml
new file mode 100644
index 0000000..5819a34
--- /dev/null
+++ b/quickshell/Procs.qml
@@ -0,0 +1,111 @@
+import Quickshell
+import Quickshell.Io
+import QtQuick
+
+Scope {
+ Timer {
+ interval: 2000
+ running: true
+ repeat: true
+ onTriggered: {
+ cpuProc.running = true
+ memProc.running = true
+ brightnessProc.running = true
+ volumeProc.running = true
+ batteryProc.running = true
+ networkProc.running = true
+ temperatureProc.running = true
+ }
+ }
+
+ Process {
+ id: cpuProc
+ command: ["sh", "-c", "head -1 /proc/stat"]
+
+ stdout: SplitParser {
+ onRead: data => {
+ var p = data.trim().split(/\s+/)
+ var idle = parseInt(p[4]) + parseInt(p[5])
+ var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0)
+ if (lastCpuTotal > 0) {
+ cpuUsage = Math.round(100 * (1 - (idle - lastCpuIdle) / (total - lastCpuTotal)))
+ }
+ lastCpuTotal = total
+ lastCpuIdle = idle
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: memProc
+ command: ["sh", "-c", "free | grep Mem"]
+ stdout: SplitParser {
+ onRead: data => {
+ var parts = data.trim().split(/\s+/)
+ var total = parseInt(parts[1]) || 1
+ var used = parseInt(parts[2]) || 0
+ memUsage = Math.round(100 * used / total)
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: temperatureProc
+ command: ["sh", "-c", "~/.config/quickshell/scripts/cpu_temp.sh"]
+ stdout: SplitParser {
+ onRead: data => {
+ cpu_temperature = parseInt(data.trim()) || 13;
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: brightnessProc
+ command: ["sh", "-c", "~/.config/quickshell/scripts/brightness.sh"]
+ stdout: SplitParser {
+ onRead: data => {
+ var parts = data.trim().split(/\s+/)
+ brightness = parseInt(parts[0]) || 100
+ temperature = parseInt(parts[1]) || 6000
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: volumeProc
+ command: ["sh", "-c", "~/.config/quickshell/scripts/volume.sh"]
+ stdout: SplitParser {
+ onRead: data => {
+ volume = parseInt(data.trim()) || 13;
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: batteryProc
+ command: ["sh", "-c", "~/.config/quickshell/scripts/battery.sh"]
+ stdout: SplitParser {
+ onRead: data => {
+ battery = parseInt(data.trim()) || 13;
+ }
+ }
+ Component.onCompleted: running = true
+ }
+
+ Process {
+ id: networkProc
+ command: ["sh", "-c", "~/.config/quickshell/scripts/network.sh"]
+ stdout: SplitParser {
+ onRead: data => {
+ var parts = data.trim().split(/\s+/)
+ wifiName = parts[0];
+ }
+ }
+ Component.onCompleted: running = true
+ }
+}
diff --git a/quickshell/items/DataBus.qml b/quickshell/items/DataBus.qml
new file mode 100644
index 0000000..ef9f952
--- /dev/null
+++ b/quickshell/items/DataBus.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Data Bus:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: cpuUsage
+ color: shellRoot.orange
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Electrons.qml b/quickshell/items/Electrons.qml
new file mode 100644
index 0000000..f3164a0
--- /dev/null
+++ b/quickshell/items/Electrons.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Electrons:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: battery
+ color: (battery <= redBatteryPoint) ? shellRoot.red : ((battery <= orangeBatteryPoint) ? shellRoot.orange : shellRoot.green)
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Latch.qml b/quickshell/items/Latch.qml
new file mode 100644
index 0000000..5cc6487
--- /dev/null
+++ b/quickshell/items/Latch.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Latch:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: memUsage
+ color: shellRoot.orange
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Photons.qml b/quickshell/items/Photons.qml
new file mode 100644
index 0000000..0ba94ee
--- /dev/null
+++ b/quickshell/items/Photons.qml
@@ -0,0 +1,32 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Photons:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: brightness
+ color: shellRoot.orange
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+ Text {
+ text: "(" + temperature + "K)"
+ color: shellRoot.red
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Radiation.qml b/quickshell/items/Radiation.qml
new file mode 100644
index 0000000..e2a6c09
--- /dev/null
+++ b/quickshell/items/Radiation.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Radiation:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: cpu_temperature + "K"
+ color: shellRoot.orange
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Radio.qml b/quickshell/items/Radio.qml
new file mode 100644
index 0000000..b3ee8e6
--- /dev/null
+++ b/quickshell/items/Radio.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Radio:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: wifiName
+ color: wifiName == "Offline" ? shellRoot.red : shellRoot.green
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: networkManagerProc.running = true
+ }
+ }
+}
diff --git a/quickshell/items/Spin.qml b/quickshell/items/Spin.qml
new file mode 100644
index 0000000..72e6105
--- /dev/null
+++ b/quickshell/items/Spin.qml
@@ -0,0 +1,45 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Spin:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ property var formatMode: "HH:mm:ss"
+
+ id: clock
+ text: Qt.formatDateTime(new Date(), clock.formatMode)
+ color: shellRoot.purple
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+
+ Timer {
+ interval: 1000
+ running: true
+ repeat: true
+ onTriggered: clock.text = Qt.formatDateTime(new Date(), clock.formatMode)
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (clock.formatMode === "HH:mm:ss") {
+ clock.formatMode = "yyyy/MM/dd, dddd"
+ } else {
+ clock.formatMode = "HH:mm:ss"
+ }
+ clock.text = Qt.formatDateTime(new Date(), clock.formatMode)
+ }
+ }
+ }
+}
diff --git a/quickshell/items/Waves.qml b/quickshell/items/Waves.qml
new file mode 100644
index 0000000..05e42e2
--- /dev/null
+++ b/quickshell/items/Waves.qml
@@ -0,0 +1,23 @@
+import QtQuick
+import QtQuick.Layouts
+
+RowLayout {
+ spacing: 0
+ Text {
+ text: " Waves:"
+ color: shellRoot.white
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ }
+ }
+ Text {
+ text: volume
+ color: shellRoot.green
+ font {
+ family: shellRoot.fontFamily
+ pixelSize: shellRoot.fontSize
+ bold: true
+ }
+ }
+}
diff --git a/quickshell/items/Workspace.qml b/quickshell/items/Workspace.qml
new file mode 100644
index 0000000..f793850
--- /dev/null
+++ b/quickshell/items/Workspace.qml
@@ -0,0 +1,22 @@
+import QtQuick
+import Quickshell.Hyprland
+
+Repeater {
+ model: 5
+
+ Text {
+ property bool isActive: Hyprland.focusedWorkspace?.id === (index + 1)
+
+ text: index + 1
+ color: isActive ? shellRoot.green : shellRoot.white
+ font {
+ pixelSize: shellRoot.fontSize;
+ bold: true;
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: Hyprland.dispatch("workspace " + (index + 1))
+ }
+ }
+}
diff --git a/quickshell/battery.sh b/quickshell/scripts/battery.sh
index c7d4302..c7d4302 100755
--- a/quickshell/battery.sh
+++ b/quickshell/scripts/battery.sh
diff --git a/quickshell/brightness.sh b/quickshell/scripts/brightness.sh
index ccb2bc2..ccb2bc2 100755
--- a/quickshell/brightness.sh
+++ b/quickshell/scripts/brightness.sh
diff --git a/quickshell/cpu_temp.sh b/quickshell/scripts/cpu_temp.sh
index de73d7d..de73d7d 100755
--- a/quickshell/cpu_temp.sh
+++ b/quickshell/scripts/cpu_temp.sh
diff --git a/quickshell/scripts/list-apps.sh b/quickshell/scripts/list-apps.sh
new file mode 100755
index 0000000..bac48b6
--- /dev/null
+++ b/quickshell/scripts/list-apps.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+# List desktop applications for the launcher
+
+# Blacklist - apps to hide (add desktop file basenames here)
+BLACKLIST=(
+ # "xfce4-about.desktop"
+ # "avahi-discover.desktop"
+ # "bssh.desktop"
+ # "bvnc.desktop"
+ # "qv4l2.desktop"
+ # "qvidcap.desktop"
+ # "lstopo.desktop"
+ # "uuctl.desktop"
+ # "codium.desktop" # Hide regular VSCodium (keep Wayland version)
+ # "xgps.desktop" # Hide Xgps
+ # "xgpsspeed.desktop" # Hide Xgpsspeed
+)
+
+# Search paths for .desktop files (local first so overrides work)
+XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
+IFS=':' read -ra SEARCH_PATHS <<< "$XDG_DATA_DIRS"
+
+# Function to find icon path
+find_icon() {
+ local icon_name="$1"
+
+ # If it's already a path, return it
+ if [[ "$icon_name" == /* ]]; then
+ echo "$icon_name"
+ return
+ fi
+
+ # Common icon sizes to try
+ local sizes=(48 32 64 128 256)
+
+ # Icon theme paths
+ local icon_paths=(
+ "$HOME/.local/share/icons"
+ "$HOME/.icons"
+ "/usr/share/icons"
+ "/usr/share/pixmaps"
+ )
+
+ # Try to find the icon
+ for path in "${icon_paths[@]}"; do
+ # Try with extensions
+ for ext in png svg xpm; do
+ # Direct match in pixmaps
+ if [ -f "$path/$icon_name.$ext" ]; then
+ echo "$path/$icon_name.$ext"
+ return
+ fi
+
+ # Try in hicolor theme with different sizes
+ for size in "${sizes[@]}"; do
+ if [ -f "$path/hicolor/${size}x${size}/apps/$icon_name.$ext" ]; then
+ echo "$path/hicolor/${size}x${size}/apps/$icon_name.$ext"
+ return
+ fi
+ done
+ done
+ done
+
+ # If not found, just return the icon name
+ echo "$icon_name"
+}
+
+# Collect all desktop files and process, avoiding duplicates
+# We use process substitution to avoid subshell issues with arrays
+declare -A seen_apps
+
+for dir in "${SEARCH_PATHS[@]}"; do
+ [ ! -d "$dir" ] && continue
+
+ while IFS= read -r desktop_file; do
+ basename_file=$(basename "$desktop_file")
+
+ # Skip duplicates (local overrides system)
+ [[ -n "${seen_apps[$basename_file]}" ]] && continue
+ seen_apps[$basename_file]=1
+
+ # Skip blacklisted
+ skip=0
+ for blacklisted in "${BLACKLIST[@]}"; do
+ if [[ "$basename_file" == "$blacklisted" ]]; then
+ skip=1
+ break
+ fi
+ done
+ [[ $skip -eq 1 ]] && continue
+
+ # Skip if NoDisplay=true
+ grep -q "^NoDisplay=true" "$desktop_file" 2>/dev/null && continue
+
+ # Extract fields
+ name=$(grep "^Name=" "$desktop_file" | head -1 | cut -d= -f2-)
+ comment=$(grep "^Comment=" "$desktop_file" | head -1 | cut -d= -f2-)
+ icon=$(grep "^Icon=" "$desktop_file" | head -1 | cut -d= -f2-)
+ exec=$(grep "^Exec=" "$desktop_file" | head -1 | cut -d= -f2- | sed 's/%[uUfF]//g' | sed 's/%[cdnNvmki]//g')
+
+ # Skip if no name or exec
+ [ -z "$name" ] && continue
+ [ -z "$exec" ] && continue
+
+ # Default comment if empty
+ [ -z "$comment" ] && comment="Application"
+
+ # Find icon path
+ icon_path=$(find_icon "$icon")
+
+ # Output in format: name|comment|icon_path|exec
+ echo "$name|$comment|$icon_path|$exec"
+
+ done < <(find -L "$dir" -name "*.desktop" -type f 2>/dev/null)
+done | sort -u
diff --git a/quickshell/network.sh b/quickshell/scripts/network.sh
index f50707e..f50707e 100755
--- a/quickshell/network.sh
+++ b/quickshell/scripts/network.sh
diff --git a/quickshell/volume.sh b/quickshell/scripts/volume.sh
index 3059255..3059255 100755
--- a/quickshell/volume.sh
+++ b/quickshell/scripts/volume.sh
diff --git a/quickshell/shell.qml b/quickshell/shell.qml
index 1ad2eb4..307dece 100644
--- a/quickshell/shell.qml
+++ b/quickshell/shell.qml
@@ -1,3 +1,4 @@
+import "./items"
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
@@ -5,13 +6,15 @@ import Quickshell.Io
import QtQuick
import QtQuick.Layouts
-PanelWindow {
- id: root
+ShellRoot {
+ id: shellRoot
property int redBatteryPoint: 15
property int orangeBatteryPoint: 30
property color black: "#000000"
+ property color gray: "#202020"
+ property color lightgray: "#404040"
property color white: "#ffffff"
property color green: "#28CD41"
property color red: "#FF4040"
@@ -21,351 +24,175 @@ PanelWindow {
property string fontFamily: "JetBrainsMono Nerd Font"
property int fontSize: 18
- property int cpuUsage: 0
- property int lastCpuTotal: 0
- property int lastCpuIdle: 0
-
- Process {
- id: cpuProc
- command: ["sh", "-c", "head -1 /proc/stat"]
-
- stdout: SplitParser {
- onRead: data => {
- var p = data.trim().split(/\s+/)
- var idle = parseInt(p[4]) + parseInt(p[5])
- var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0)
- if (lastCpuTotal > 0) {
- cpuUsage = Math.round(100 * (1 - (idle - lastCpuIdle) / (total - lastCpuTotal)))
- }
- lastCpuTotal = total
- lastCpuIdle = idle
- }
- }
- Component.onCompleted: running = true
- }
-
- property int memUsage: 0
- Process {
- id: memProc
- command: ["sh", "-c", "free | grep Mem"]
- stdout: SplitParser {
- onRead: data => {
- var parts = data.trim().split(/\s+/)
- var total = parseInt(parts[1]) || 1
- var used = parseInt(parts[2]) || 0
- memUsage = Math.round(100 * used / total)
- }
- }
- Component.onCompleted: running = true
- }
-
- property int cpu_temperature: 6000
- Process {
- id: temperatureProc
- command: ["sh", "-c", "~/.config/quickshell/cpu_temp.sh"]
- stdout: SplitParser {
- onRead: data => {
- cpu_temperature = parseInt(data.trim()) || 13;
- }
- }
- Component.onCompleted: running = true
- }
-
- property int brightness: 100
- property int temperature: 6000
- Process {
- id: brightnessProc
- command: ["sh", "-c", "~/.config/quickshell/brightness.sh"]
- stdout: SplitParser {
- onRead: data => {
- var parts = data.trim().split(/\s+/)
- brightness = parseInt(parts[0]) || 100
- temperature = parseInt(parts[1]) || 6000
- }
- }
- Component.onCompleted: running = true
- }
-
- property int volume: 12
- Process {
- id: volumeProc
- command: ["sh", "-c", "~/.config/quickshell/volume.sh"]
- stdout: SplitParser {
- onRead: data => {
- volume = parseInt(data.trim()) || 13;
- }
- }
- Component.onCompleted: running = true
- }
-
- property int battery: 12
- Process {
- id: batteryProc
- command: ["sh", "-c", "~/.config/quickshell/battery.sh"]
- stdout: SplitParser {
- onRead: data => {
- battery = parseInt(data.trim()) || 13;
- }
+ property bool appLauncherVisible: false
+
+ // focus: true
+ // Keys.onPressed: (event) => {
+ // if (event.key === Qt.Key_R && event.modifiers & Qt.MetaModifier) {
+ // Quickshell.execDetached(["notify-send", "ok"])
+ // appLauncherVisible = true;
+ // }
+ // }
+ // Component.onCompleted: {
+ // Hyprland.rawSignals = true;
+ // }
+ // Connections {
+ // target: Hyprland
+ //
+ // function onEvent(sig) {
+ // Quickshell.execDetached(["notify-send", "ok"])
+ // console.log("Raw = ", sig);
+ // }
+ // }
+ GlobalShortcut {
+ id: launcherShortcut
+ name: "toggleLauncher"
+ onPressed: {
+ appLauncherVisible = true
}
- Component.onCompleted: running = true
}
- property string wifiName: "Offline"
- Process {
- id: networkProc
- command: ["sh", "-c", "~/.config/quickshell/network.sh"]
- stdout: SplitParser {
- onRead: data => {
- var parts = data.trim().split(/\s+/)
- wifiName = parts[0];
- }
- }
- Component.onCompleted: running = true
- }
-
- Timer {
- interval: 2000
- running: true
- repeat: true
- onTriggered: {
- cpuProc.running = true
- memProc.running = true
- brightnessProc.running = true
- volumeProc.running = true
- batteryProc.running = true
- networkProc.running = true
- temperatureProc.running = true
- }
- }
- Process {
- id: networkManagerProc
- command: ["foot", "-e", "nmtui"]
- Component.onCompleted: running = false
- }
-
- anchors.top: true
- anchors.left: true
- anchors.right: true
- implicitHeight: 32
- color: root.black
-
- RowLayout {
- anchors.fill: parent
- anchors.margins: 0
-
- Repeater {
- model: 5
-
- Text {
- property bool isActive: Hyprland.focusedWorkspace?.id === (index + 1)
-
- text: index + 1
- color: isActive ? root.green : root.white
- font {
- pixelSize: root.fontSize;
- bold: true;
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: Hyprland.dispatch("workspace " + (index + 1))
- }
- }
- }
- Item { Layout.fillWidth: true }
- RowLayout {
- spacing: 0
- Text {
- text: " Radio:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: wifiName
- color: wifiName == "Offline" ? root.red : root.green
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: networkManagerProc.running = true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Electrons:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: battery
- color: (battery <= redBatteryPoint) ? root.red : ((battery <= orangeBatteryPoint) ? root.orange : root.green)
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Waves:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: volume
- color: root.green
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Photons:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: brightness
- color: root.orange
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- Text {
- text: "(" + temperature + "K)"
- color: root.red
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Radiation:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: cpu_temperature + "K"
- color: root.orange
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Data Bus:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: cpuUsage
- color: root.orange
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
- }
- }
- RowLayout {
- spacing: 0
- Text {
- text: " Latch:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- text: memUsage
- color: root.orange
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
+ Variants {
+ model: Quickshell.screens
+ PanelWindow {
+ id: root
+
+ property int cpuUsage: 0
+ property int lastCpuTotal: 0
+ property int lastCpuIdle: 0
+ property int memUsage: 0
+ property int cpu_temperature: 6000
+ property int brightness: 100
+ property int temperature: 6000
+ property int volume: 12
+ property int battery: 12
+ property string wifiName: "Offline"
+
+ Procs {}
+ Process {
+ id: networkManagerProc
+ command: ["foot", "-e", "nmtui"]
+ Component.onCompleted: running = false
+ }
+
+ anchors.top: true
+ anchors.left: true
+ anchors.right: true
+ implicitHeight: 32
+ color: shellRoot.black
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: 0
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+
+ Workspace {}
+ Item { Layout.fillWidth: true }
+ Radio {}
+ Electrons {}
+ Waves {}
+ Photons {}
+ Radiation {}
+ DataBus {}
+ Latch {}
+ Spin {}
+ }
+
+ PanelWindow {
+ id: apps
+
+ anchors.top: true
+ anchors.right: true
+ anchors.bottom: true
+ implicitWidth: 32
+ color: shellRoot.black
+
+ ColumnLayout {
+ IconButton {
+ icon: ""
+ tooltip: "Firefox"
+ onClicked: Quickshell.execDetached(["firefox"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "Dolphin"
+ onClicked: Quickshell.execDetached(["dolphin"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "Vscode"
+ onClicked: Quickshell.execDetached(["code"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "Localsend"
+ onClicked: Quickshell.execDetached(["localsend_app"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "Bitwarden"
+ onClicked: Quickshell.execDetached(["bitwarden"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "GIMP"
+ onClicked: Quickshell.execDetached(["gimp"])
+ }
+ IconButton {
+ icon: "󰠮"
+ tooltip: "Obsidian"
+ onClicked: Quickshell.execDetached(["obsidian"])
+ }
+ IconButton {
+ icon: ""
+ tooltip: "Discord"
+ onClicked: Quickshell.execDetached(["discord"])
+ }
+ IconButton {
+ icon: "󰕼"
+ tooltip: "VLC"
+ onClicked: Quickshell.execDetached(["vlc"])
+ }
}
}
}
- RowLayout {
- spacing: 0
- Text {
- text: " Spin:"
- color: root.white
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- }
- }
- Text {
- property var formatMode: "HH:mm:ss"
-
- id: clock
- text: Qt.formatDateTime(new Date(), clock.formatMode)
- color: root.purple
- font {
- family: root.fontFamily
- pixelSize: root.fontSize
- bold: true
- }
-
- Timer {
- interval: 1000
- running: true
- repeat: true
- onTriggered: clock.text = Qt.formatDateTime(new Date(), clock.formatMode)
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- if (clock.formatMode === "HH:mm:ss") {
- clock.formatMode = "yyyy/MM/dd, dddd"
- } else {
- clock.formatMode = "HH:mm:ss"
- }
- clock.text = Qt.formatDateTime(new Date(), clock.formatMode)
- }
+ }
+ Variants {
+ model: Quickshell.screens
+
+ PanelWindow {
+ visible: shellRoot.appLauncherVisible
+
+ anchors {
+ top: true
+ left: true
+ }
+
+ margins {
+ top: Screen.height/2 - 300
+ left: Screen.width/2 - 200
+ }
+
+ implicitWidth: 400
+ implicitHeight: 600
+
+ color: "transparent"
+ exclusiveZone: 0
+
+ WlrLayershell.layer: WlrLayer.Overlay
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
+
+ Behavior on height {
+ NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
+ }
+
+ AppLauncher {
+ anchors.fill: parent
+ isVisible: shellRoot.appLauncherVisible
+
+ onRequestClose: {
+ shellRoot.appLauncherVisible = false
}
}
}