1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-28 15:32:50 -05:00

Use proc helper in more places

This commit is contained in:
bbedward
2025-10-14 16:52:50 -04:00
parent f9cb0506e9
commit 5f95fa5e79
10 changed files with 270 additions and 410 deletions

View File

@@ -82,7 +82,46 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", stateDir]) Quickshell.execDetached(["mkdir", "-p", stateDir])
matugenCheck.running = true Proc.runCommand("matugenCheck", ["which", "matugen"], (output, code) => {
matugenAvailable = (code === 0) && !envDisableMatugen
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
if (!matugenAvailable || isGreeterMode) {
return
}
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
if (currentTheme === dynamic) {
if (wallpaperPath) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
if (wallpaperPath.startsWith("#")) {
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
} else {
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
}
}
} else {
let primaryColor
let matugenType
if (currentTheme === "custom") {
if (customThemeData && customThemeData.primary) {
primaryColor = customThemeData.primary
matugenType = customThemeData.matugen_type
}
} else {
primaryColor = currentThemeData.primary
matugenType = currentThemeData.matugen_type
}
if (primaryColor) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
}
}
}, 0)
if (typeof SessionData !== "undefined") { if (typeof SessionData !== "undefined") {
SessionData.isLightModeChanged.connect(root.onLightModeChanged) SessionData.isLightModeChanged.connect(root.onLightModeChanged)
} }
@@ -669,8 +708,17 @@ Singleton {
} }
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "true" : "false" const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "true" : "false"
gtkApplier.command = [shellDir + "/scripts/gtk.sh", configDir, isLight, shellDir] Proc.runCommand("gtkApplier", [shellDir + "/scripts/gtk.sh", configDir, isLight, shellDir], (output, exitCode) => {
gtkApplier.running = true if (exitCode === 0) {
if (typeof ToastService !== "undefined" && typeof NiriService !== "undefined" && !NiriService.matugenSuppression) {
ToastService.showInfo("GTK colors applied successfully")
}
} else {
if (typeof ToastService !== "undefined") {
ToastService.showError("Failed to apply GTK colors")
}
}
})
} }
function applyQtColors() { function applyQtColors() {
@@ -681,8 +729,17 @@ Singleton {
return return
} }
qtApplier.command = [shellDir + "/scripts/qt.sh", configDir] Proc.runCommand("qtApplier", [shellDir + "/scripts/qt.sh", configDir], (output, exitCode) => {
qtApplier.running = true if (exitCode === 0) {
if (typeof ToastService !== "undefined") {
ToastService.showInfo("Qt colors applied successfully")
}
} else {
if (typeof ToastService !== "undefined") {
ToastService.showError("Failed to apply Qt colors")
}
}
})
} }
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); } function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); }
@@ -750,57 +807,6 @@ Singleton {
Process {
id: matugenCheck
command: ["which", "matugen"]
onExited: code => {
matugenAvailable = (code === 0) && !envDisableMatugen
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
if (!matugenAvailable || isGreeterMode) {
return
}
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
if (currentTheme === dynamic) {
if (wallpaperPath) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
if (wallpaperPath.startsWith("#")) {
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
} else {
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
}
}
} else {
let primaryColor
let matugenType
if (currentTheme === "custom") {
if (customThemeData && customThemeData.primary) {
primaryColor = customThemeData.primary
matugenType = customThemeData.matugen_type
}
} else {
primaryColor = currentThemeData.primary
matugenType = currentThemeData.matugen_type
}
if (primaryColor) {
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
}
}
}
}
Process {
id: ensureStateDir
}
Process { Process {
id: systemThemeGenerator id: systemThemeGenerator
running: false running: false
@@ -817,56 +823,6 @@ Singleton {
} }
} }
Process {
id: gtkApplier
running: false
stdout: StdioCollector {
id: gtkStdout
}
stderr: StdioCollector {
id: gtkStderr
}
onExited: exitCode => {
if (exitCode === 0) {
if (typeof ToastService !== "undefined" && typeof NiriService !== "undefined" && !NiriService.matugenSuppression) {
ToastService.showInfo("GTK colors applied successfully")
}
} else {
if (typeof ToastService !== "undefined") {
ToastService.showError("Failed to apply GTK colors: " + gtkStderr.text)
}
}
}
}
Process {
id: qtApplier
running: false
stdout: StdioCollector {
id: qtStdout
}
stderr: StdioCollector {
id: qtStderr
}
onExited: exitCode => {
if (exitCode === 0) {
if (typeof ToastService !== "undefined") {
ToastService.showInfo("Qt colors applied successfully")
}
} else {
if (typeof ToastService !== "undefined") {
ToastService.showError("Failed to apply Qt colors: " + qtStderr.text)
}
}
}
}
FileView { FileView {
id: customThemeFileView id: customThemeFileView
watchChanges: currentTheme === "custom" watchChanges: currentTheme === "custom"

View File

@@ -66,7 +66,13 @@ Item {
property bool initialized: false property bool initialized: false
sourceComponent: DankBar { sourceComponent: DankBar {
onColorPickerRequested: colorPickerModal.show() onColorPickerRequested: {
if (colorPickerModal.shouldBeVisible) {
colorPickerModal.close()
} else {
colorPickerModal.show()
}
}
} }
Component.onCompleted: { Component.onCompleted: {

View File

@@ -2,17 +2,16 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Modals.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { DankModal {
id: root id: root
property string pickerTitle: "Choose Color" property string pickerTitle: "Choose Color"
property color selectedColor: Theme.primary property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
property bool shouldBeVisible: false
property var onColorSelectedCallback: null property var onColorSelectedCallback: null
signal colorSelected(color selectedColor) signal colorSelected(color selectedColor)
@@ -25,23 +24,24 @@ PanelWindow {
property real gradientX: 0 property real gradientX: 0
property real gradientY: 0 property real gradientY: 0
function open() { readonly property var standardColors: [
currentColor = selectedColor "#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4",
updateFromColor(currentColor) "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722",
shouldBeVisible = true "#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7",
Qt.callLater(() => colorContent.forceActiveFocus()) "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19",
} "#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f",
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315",
function close() { "#ffffff", "#9e9e9e", "#212121"
shouldBeVisible = false ]
onColorSelectedCallback = null
}
function show() { function show() {
currentColor = selectedColor
updateFromColor(currentColor)
open() open()
} }
function hide() { function hide() {
onColorSelectedCallback = null
close() close()
} }
@@ -74,96 +74,50 @@ PanelWindow {
saturation = Math.max(0, Math.min(1, x)) saturation = Math.max(0, Math.min(1, x))
value = Math.max(0, Math.min(1, 1 - y)) value = Math.max(0, Math.min(1, 1 - y))
updateColor() updateColor()
selectedColor = currentColor
} }
function pickColorFromScreen() { function pickColorFromScreen() {
close() hide()
hyprpickerProcess.running = true Proc.runCommand("hyprpicker", ["hyprpicker", "--format=hex"], (output, errorCode) => {
} if (errorCode !== 0) {
console.warn("hyprpicker exited with code:", errorCode)
Process { root.show()
id: hyprpickerProcess return
running: false
command: ["hyprpicker", "--format=hex"]
stdout: SplitParser {
onRead: data => {
const colorStr = data.trim()
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
root.currentColor = colorStr
root.updateFromColor(root.currentColor)
hexInput.text = root.currentColor.toString()
copyColorToClipboard(colorStr)
root.open()
}
} }
} const colorStr = output.trim()
if (colorStr.length >= 7 && colorStr.startsWith('#')) {
onExited: (exitCode, exitStatus) => { const pickedColor = Qt.color(colorStr)
if (exitCode !== 0) { root.selectedColor = pickedColor
console.warn("hyprpicker exited with code:", exitCode) root.currentColor = pickedColor
root.updateFromColor(pickedColor)
copyColorToClipboard(colorStr)
root.show()
} }
root.open() })
}
} }
readonly property var standardColors: [ width: 680
"#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", height: 680
"#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", backgroundColor: Theme.surfaceContainer
"#d32f2f", "#c2185b", "#7b1fa2", "#512da8", "#303f9f", "#1976d2", "#0288d1", "#0097a7", cornerRadius: Theme.cornerRadius
"#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", borderColor: Theme.outlineMedium
"#c62828", "#ad1457", "#6a1b9a", "#4527a0", "#283593", "#1565c0", "#0277bd", "#00838f", borderWidth: 1
"#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", keepContentLoaded: true
"#ffffff", "#9e9e9e", "#212121"
]
visible: shouldBeVisible onBackgroundClicked: hide()
WlrLayershell.namespace: "quickshell:color-picker"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
MouseArea {
anchors.fill: parent
onClicked: root.close()
Rectangle {
color: "#80000000"
anchors.fill: parent
}
}
Rectangle {
anchors.centerIn: parent
width: 680
height: 680
radius: Theme.cornerRadius
color: Theme.surfaceContainer
border.color: Theme.outlineMedium
border.width: 1
MouseArea {
anchors.fill: parent
onClicked: {} // Prevent clicks from propagating to background
}
content: Component {
FocusScope { FocusScope {
id: colorContent id: colorContent
property alias hexInput: hexInput
anchors.fill: parent anchors.fill: parent
focus: root.shouldBeVisible focus: true
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
root.close() root.hide()
event.accepted = true event.accepted = true
} }
@@ -199,7 +153,7 @@ PanelWindow {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
pickColorFromScreen() root.pickColorFromScreen()
} }
} }
@@ -208,7 +162,7 @@ PanelWindow {
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: () => { onClicked: () => {
root.close() root.hide()
} }
} }
} }
@@ -329,12 +283,14 @@ PanelWindow {
const h = Math.max(0, Math.min(1, mouse.y / height)) const h = Math.max(0, Math.min(1, mouse.y / height))
root.hue = h root.hue = h
root.updateColor() root.updateColor()
root.selectedColor = root.currentColor
} }
onPositionChanged: mouse => { onPositionChanged: mouse => {
if (pressed) { if (pressed) {
const h = Math.max(0, Math.min(1, mouse.y / height)) const h = Math.max(0, Math.min(1, mouse.y / height))
root.hue = h root.hue = h
root.updateColor() root.updateColor()
root.selectedColor = root.currentColor
} }
} }
} }
@@ -373,8 +329,10 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: () => { onClicked: () => {
root.currentColor = modelData const pickedColor = Qt.color(modelData)
root.updateFromColor(root.currentColor) root.selectedColor = pickedColor
root.currentColor = pickedColor
root.updateFromColor(pickedColor)
} }
} }
} }
@@ -429,8 +387,10 @@ PanelWindow {
enabled: index < SessionData.recentColors.length enabled: index < SessionData.recentColors.length
onClicked: () => { onClicked: () => {
if (index < SessionData.recentColors.length) { if (index < SessionData.recentColors.length) {
root.currentColor = SessionData.recentColors[index] const pickedColor = SessionData.recentColors[index]
root.updateFromColor(root.currentColor) root.selectedColor = pickedColor
root.currentColor = pickedColor
root.updateFromColor(pickedColor)
} }
} }
} }
@@ -459,6 +419,7 @@ PanelWindow {
onSliderValueChanged: (newValue) => { onSliderValueChanged: (newValue) => {
root.alpha = newValue / 100 root.alpha = newValue / 100
root.updateColor() root.updateColor()
root.selectedColor = root.currentColor
} }
} }
} }
@@ -509,6 +470,7 @@ PanelWindow {
if (!hexPattern.test(text)) return if (!hexPattern.test(text)) return
const color = Qt.color(text) const color = Qt.color(text)
if (color) { if (color) {
root.selectedColor = color
root.currentColor = color root.currentColor = color
root.updateFromColor(color) root.updateFromColor(color)
} }
@@ -530,9 +492,9 @@ PanelWindow {
root.currentColor = color root.currentColor = color
root.updateFromColor(color) root.updateFromColor(color)
root.selectedColor = root.currentColor root.selectedColor = root.currentColor
colorSelected(root.currentColor) root.colorSelected(root.currentColor)
SessionData.addRecentColor(root.currentColor) SessionData.addRecentColor(root.currentColor)
root.close() root.hide()
} }
} }
} }
@@ -549,8 +511,8 @@ PanelWindow {
backgroundColor: "transparent" backgroundColor: "transparent"
textColor: Theme.surfaceText textColor: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: root.close() onClicked: root.hide()
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
@@ -570,7 +532,7 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onClicked: { onClicked: {
const colorString = root.currentColor.toString() const colorString = root.currentColor.toString()
copyColorToClipboard(colorString) root.copyColorToClipboard(colorString)
} }
} }
} }

View File

@@ -123,4 +123,4 @@ Rectangle {
toggleBatteryPopup(); toggleBatteryPopup();
} }
} }
} }

View File

@@ -98,29 +98,6 @@ Rectangle {
} }
Process {
id: hyprlandLayoutProcess
running: false
command: ["hyprctl", "-j", "devices"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
// Find the main keyboard and get its active keymap
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
root.hyprlandKeyboard = mainKeyboard.name
if (mainKeyboard && mainKeyboard.active_keymap) {
root.currentLayout = mainKeyboard.active_keymap
} else {
root.currentLayout = "Unknown"
}
} catch (e) {
root.currentLayout = "Unknown"
}
}
}
}
Timer { Timer {
id: updateTimer id: updateTimer
interval: 1000 interval: 1000
@@ -139,7 +116,24 @@ Rectangle {
if (CompositorService.isNiri) { if (CompositorService.isNiri) {
root.currentLayout = NiriService.getCurrentKeyboardLayoutName() root.currentLayout = NiriService.getCurrentKeyboardLayoutName()
} else if (CompositorService.isHyprland) { } else if (CompositorService.isHyprland) {
hyprlandLayoutProcess.running = true Proc.runCommand("hyprlandLayout", ["hyprctl", "-j", "devices"], (output, exitCode) => {
if (exitCode !== 0) {
root.currentLayout = "Unknown"
return
}
try {
const data = JSON.parse(output)
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
root.hyprlandKeyboard = mainKeyboard.name
if (mainKeyboard && mainKeyboard.active_keymap) {
root.currentLayout = mainKeyboard.active_keymap
} else {
root.currentLayout = "Unknown"
}
} catch (e) {
root.currentLayout = "Unknown"
}
})
} }
} }
} }

View File

@@ -275,7 +275,7 @@ Column {
// Match count display // Match count display
StyledText { StyledText {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
text: matchCount > 0 ? I18n.tr("%1/%2").arg(currentMatchIndex + 1).arg(matchCount) : searchQuery.length > 0 ? I18n.tr("No matches") : "" text: matchCount > 0 ? "%1/%2".arg(currentMatchIndex + 1).arg(matchCount) : searchQuery.length > 0 ? I18n.tr("No matches") : ""
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: matchCount > 0 ? Theme.primary : Theme.surfaceTextMedium color: matchCount > 0 ? Theme.primary : Theme.surfaceTextMedium
visible: searchQuery.length > 0 visible: searchQuery.length > 0

View File

@@ -4,9 +4,9 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common
Singleton { Singleton {
id: root id: root
@@ -160,7 +160,20 @@ Singleton {
} }
if (niriSocket && niriSocket.length > 0) { if (niriSocket && niriSocket.length > 0) {
niriSocketCheck.running = true Proc.runCommand("niriSocketCheck", ["test", "-S", root.niriSocket], (output, exitCode) => {
if (exitCode === 0) {
root.isNiri = true
root.isHyprland = false
root.compositor = "niri"
console.log("CompositorService: Detected Niri with socket:", root.niriSocket)
NiriService.generateNiriBinds()
} else {
root.isHyprland = false
root.isNiri = true
root.compositor = "niri"
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
}
}, 0)
} else { } else {
isHyprland = false isHyprland = false
isNiri = false isNiri = false
@@ -188,24 +201,4 @@ Singleton {
} }
console.warn("CompositorService: Cannot power on monitors, unknown compositor") console.warn("CompositorService: Cannot power on monitors, unknown compositor")
} }
Process {
id: niriSocketCheck
command: ["test", "-S", root.niriSocket]
onExited: exitCode => {
if (exitCode === 0) {
root.isNiri = true
root.isHyprland = false
root.compositor = "niri"
console.log("CompositorService: Detected Niri with socket:", root.niriSocket)
NiriService.generateNiriBinds()
} else {
root.isHyprland = false
root.isNiri = true
root.compositor = "niri"
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
}
}
}
} }

View File

@@ -4,7 +4,7 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import qs.Common
Singleton { Singleton {
id: root id: root
@@ -18,11 +18,62 @@ Singleton {
property bool profileAvailable: false property bool profileAvailable: false
function getUserInfo() { function getUserInfo() {
userInfoProcess.running = true Proc.runCommand("userInfo", ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""], (output, exitCode) => {
if (exitCode !== 0) {
root.username = "User"
root.fullName = "User"
root.hostname = "System"
return
}
const parts = output.trim().split("|")
if (parts.length >= 3) {
root.username = parts[0] || ""
root.fullName = parts[1] || parts[0] || ""
root.hostname = parts[2] || ""
}
}, 0)
} }
function getUptime() { function getUptime() {
uptimeProcess.running = true Proc.runCommand("uptime", ["cat", "/proc/uptime"], (output, exitCode) => {
if (exitCode !== 0) {
root.uptime = "Unknown"
return
}
const seconds = parseInt(output.split(" ")[0])
const days = Math.floor(seconds / 86400)
const hours = Math.floor((seconds % 86400) / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const parts = []
if (days > 0) {
parts.push(`${days} day${days === 1 ? "" : "s"}`)
}
if (hours > 0) {
parts.push(`${hours} hour${hours === 1 ? "" : "s"}`)
}
if (minutes > 0) {
parts.push(`${minutes} minute${minutes === 1 ? "" : "s"}`)
}
if (parts.length > 0) {
root.uptime = `up ${parts.join(", ")}`
} else {
root.uptime = `up ${seconds} seconds`
}
let shortUptime = "up"
if (days > 0) {
shortUptime += ` ${days}d`
}
if (hours > 0) {
shortUptime += ` ${hours}h`
}
if (minutes > 0) {
shortUptime += ` ${minutes}m`
}
root.shortUptime = shortUptime
}, 0)
} }
function refreshUserInfo() { function refreshUserInfo() {
@@ -34,82 +85,4 @@ Singleton {
getUserInfo() getUserInfo()
getUptime() getUptime()
} }
Process {
id: userInfoProcess
command: ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""]
running: false
onExited: exitCode => {
if (exitCode !== 0) {
root.username = "User"
root.fullName = "User"
root.hostname = "System"
}
}
stdout: StdioCollector {
onStreamFinished: {
const parts = text.trim().split("|")
if (parts.length >= 3) {
root.username = parts[0] || ""
root.fullName = parts[1] || parts[0] || ""
root.hostname = parts[2] || ""
}
}
}
}
Process {
id: uptimeProcess
command: ["cat", "/proc/uptime"]
running: false
onExited: exitCode => {
if (exitCode !== 0) {
root.uptime = "Unknown"
}
}
stdout: StdioCollector {
onStreamFinished: {
const seconds = parseInt(text.split(" ")[0])
const days = Math.floor(seconds / 86400)
const hours = Math.floor((seconds % 86400) / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const parts = []
if (days > 0) {
parts.push(`${days} day${days === 1 ? "" : "s"}`)
}
if (hours > 0) {
parts.push(`${hours} hour${hours === 1 ? "" : "s"}`)
}
if (minutes > 0) {
parts.push(`${minutes} minute${minutes === 1 ? "" : "s"}`)
}
if (parts.length > 0) {
root.uptime = `up ${parts.join(", ")}`
} else {
root.uptime = `up ${seconds} seconds`
}
// Create short uptime format
let shortUptime = "up"
if (days > 0) {
shortUptime += ` ${days}d`
}
if (hours > 0) {
shortUptime += ` ${hours}h`
}
if (minutes > 0) {
shortUptime += ` ${minutes}m`
}
root.shortUptime = shortUptime
}
}
}
} }

View File

@@ -1,6 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell.Io
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
@@ -31,8 +30,6 @@ Item {
function resetSearchState() { function resetSearchState() {
locationSearchTimer.stop() locationSearchTimer.stop()
dropdownHideTimer.stop() dropdownHideTimer.stop()
if (locationSearcher.running)
locationSearcher.running = false
isLoading = false isLoading = false
searchResultsModel.clear() searchResultsModel.clear()
} }
@@ -52,17 +49,52 @@ Item {
repeat: false repeat: false
onTriggered: { onTriggered: {
if (locationInput.text.length > 2) { if (locationInput.text.length > 2) {
if (locationSearcher.running)
locationSearcher.running = false
searchResultsModel.clear() searchResultsModel.clear()
root.isLoading = true root.isLoading = true
const searchLocation = locationInput.text const searchLocation = locationInput.text
root.currentSearchText = searchLocation root.currentSearchText = searchLocation
const encodedLocation = encodeURIComponent(searchLocation) const encodedLocation = encodeURIComponent(searchLocation)
const curlCommand = `curl -4 -s --connect-timeout 5 --max-time 10 'https://nominatim.openstreetmap.org/search?q=${encodedLocation}&format=json&limit=5&addressdetails=1'` const curlCommand = `curl -4 -s --connect-timeout 5 --max-time 10 'https://nominatim.openstreetmap.org/search?q=${encodedLocation}&format=json&limit=5&addressdetails=1'`
locationSearcher.command = ["bash", "-c", curlCommand] Proc.runCommand("locationSearch", ["bash", "-c", curlCommand], (output, exitCode) => {
locationSearcher.running = true root.isLoading = false
if (exitCode !== 0) {
searchResultsModel.clear()
return
}
if (root.currentSearchText !== locationInput.text)
return
const raw = output.trim()
searchResultsModel.clear()
if (!raw || raw[0] !== "[") {
return
}
try {
const data = JSON.parse(raw)
if (data.length === 0) {
return
}
for (var i = 0; i < Math.min(data.length, 5); i++) {
const location = data[i]
if (location.display_name && location.lat && location.lon) {
const parts = location.display_name.split(', ')
let cleanName = parts[0]
if (parts.length > 1) {
const state = parts[parts.length - 2]
if (state && state !== cleanName)
cleanName += `, ${state}`
}
const query = `${location.lat},${location.lon}`
searchResultsModel.append({
"name": cleanName,
"query": query
})
}
}
} catch (e) {
}
})
} }
} }
} }
@@ -79,58 +111,6 @@ Item {
} }
} }
Process {
id: locationSearcher
command: ["bash", "-c", "echo"]
running: false
onExited: exitCode => {
root.isLoading = false
if (exitCode !== 0) {
searchResultsModel.clear()
}
}
stdout: StdioCollector {
onStreamFinished: {
if (root.currentSearchText !== locationInput.text)
return
const raw = text.trim()
root.isLoading = false
searchResultsModel.clear()
if (!raw || raw[0] !== "[") {
return
}
try {
const data = JSON.parse(raw)
if (data.length === 0) {
return
}
for (var i = 0; i < Math.min(data.length, 5); i++) {
const location = data[i]
if (location.display_name && location.lat && location.lon) {
const parts = location.display_name.split(', ')
let cleanName = parts[0]
if (parts.length > 1) {
const state = parts[parts.length - 2]
if (state && state !== cleanName)
cleanName += `, ${state}`
}
const query = `${location.lat},${location.lon}`
searchResultsModel.append({
"name": cleanName,
"query": query
})
}
}
} catch (e) {
}
}
}
}
Item { Item {
id: searchInputField id: searchInputField

View File

@@ -1,7 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Io
import Quickshell.Widgets import Quickshell.Widgets
import qs.Common import qs.Common
@@ -16,19 +15,16 @@ IconImage {
asynchronous: true asynchronous: true
layer.enabled: hasColorOverride layer.enabled: hasColorOverride
Process { Component.onCompleted: {
running: true Proc.runCommand("systemLogo", ["sh", "-c", ". /etc/os-release && echo $LOGO"], (output, exitCode) => {
command: ["sh", "-c", ". /etc/os-release && echo $LOGO"] if (exitCode !== 0) return
const logo = output.trim()
stdout: StdioCollector { if (logo === "cachyos") {
onStreamFinished: () => { source = "file:///usr/share/icons/cachyos.svg"
if (text.trim() === "cachyos") { return
source = "file:///usr/share/icons/cachyos.svg"
return
}
source = Quickshell.iconPath(text.trim(), true)
} }
} source = Quickshell.iconPath(logo, true)
}, 0)
} }
layer.effect: MultiEffect { layer.effect: MultiEffect {