mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
meta: many resource usage improvements and consolidations
This commit is contained in:
41
Common/CacheUtils.qml
Normal file
41
Common/CacheUtils.qml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Clear all image cache
|
||||||
|
function clearImageCache() {
|
||||||
|
Quickshell.execDetached(["rm", "-rf", Paths.stringify(Paths.imagecache)])
|
||||||
|
Paths.mkdir(Paths.imagecache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache older than specified minutes
|
||||||
|
function clearOldCache(ageInMinutes) {
|
||||||
|
Quickshell.execDetached(["find", Paths.stringify(Paths.imagecache), "-name", "*.png", "-mmin", `+${ageInMinutes}`, "-delete"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache for specific size
|
||||||
|
function clearCacheForSize(size) {
|
||||||
|
Quickshell.execDetached(["find", Paths.stringify(Paths.imagecache), "-name", `*@${size}x${size}.png`, "-delete"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cache size in MB
|
||||||
|
function getCacheSize(callback) {
|
||||||
|
var process = Qt.createQmlObject(`
|
||||||
|
import Quickshell.Io
|
||||||
|
Process {
|
||||||
|
command: ["du", "-sm", "${Paths.stringify(Paths.imagecache)}"]
|
||||||
|
running: true
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
var sizeMB = parseInt(text.split("\\t")[0]) || 0
|
||||||
|
callback(sizeMB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Common/Paths.qml
Normal file
42
Common/Paths.qml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property url home: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
||||||
|
readonly property url pictures: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
|
||||||
|
|
||||||
|
readonly property url data: `${StandardPaths.standardLocations(StandardPaths.GenericDataLocation)[0]}/DankMaterialShell`
|
||||||
|
readonly property url state: `${StandardPaths.standardLocations(StandardPaths.GenericStateLocation)[0]}/DankMaterialShell`
|
||||||
|
readonly property url cache: `${StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0]}/DankMaterialShell`
|
||||||
|
readonly property url config: `${StandardPaths.standardLocations(StandardPaths.GenericConfigLocation)[0]}/DankMaterialShell`
|
||||||
|
|
||||||
|
readonly property url imagecache: `${cache}/imagecache`
|
||||||
|
|
||||||
|
function stringify(path: url): string {
|
||||||
|
return path.toString().replace(/%20/g, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandTilde(path: string): string {
|
||||||
|
return strip(path.replace("~", stringify(root.home)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function shortenHome(path: string): string {
|
||||||
|
return path.replace(strip(root.home), "~");
|
||||||
|
}
|
||||||
|
|
||||||
|
function strip(path: url): string {
|
||||||
|
return stringify(path).replace("file://", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkdir(path: url): void {
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", strip(path)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(from: url, to: url): void {
|
||||||
|
Quickshell.execDetached(["cp", strip(from), strip(to)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,6 @@ Singleton {
|
|||||||
property real osLogoBrightness: 0.5
|
property real osLogoBrightness: 0.5
|
||||||
property real osLogoContrast: 1.0
|
property real osLogoContrast: 1.0
|
||||||
property string wallpaperPath: ""
|
property string wallpaperPath: ""
|
||||||
property string wallpaperDirectory: StandardPaths.writableLocation(StandardPaths.PicturesLocation) + "/Wallpapers"
|
|
||||||
property bool wallpaperDynamicTheming: true
|
property bool wallpaperDynamicTheming: true
|
||||||
property string wallpaperLastPath: ""
|
property string wallpaperLastPath: ""
|
||||||
property string profileLastPath: ""
|
property string profileLastPath: ""
|
||||||
@@ -83,7 +82,6 @@ Singleton {
|
|||||||
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5;
|
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5;
|
||||||
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1.0;
|
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1.0;
|
||||||
wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : "";
|
wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : "";
|
||||||
wallpaperDirectory = settings.wallpaperDirectory !== undefined ? settings.wallpaperDirectory : StandardPaths.writableLocation(StandardPaths.PicturesLocation) + "/Wallpapers";
|
|
||||||
wallpaperDynamicTheming = settings.wallpaperDynamicTheming !== undefined ? settings.wallpaperDynamicTheming : true;
|
wallpaperDynamicTheming = settings.wallpaperDynamicTheming !== undefined ? settings.wallpaperDynamicTheming : true;
|
||||||
wallpaperLastPath = settings.wallpaperLastPath !== undefined ? settings.wallpaperLastPath : "";
|
wallpaperLastPath = settings.wallpaperLastPath !== undefined ? settings.wallpaperLastPath : "";
|
||||||
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : "";
|
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : "";
|
||||||
@@ -130,7 +128,6 @@ Singleton {
|
|||||||
"osLogoBrightness": osLogoBrightness,
|
"osLogoBrightness": osLogoBrightness,
|
||||||
"osLogoContrast": osLogoContrast,
|
"osLogoContrast": osLogoContrast,
|
||||||
"wallpaperPath": wallpaperPath,
|
"wallpaperPath": wallpaperPath,
|
||||||
"wallpaperDirectory": wallpaperDirectory,
|
|
||||||
"wallpaperDynamicTheming": wallpaperDynamicTheming,
|
"wallpaperDynamicTheming": wallpaperDynamicTheming,
|
||||||
"wallpaperLastPath": wallpaperLastPath,
|
"wallpaperLastPath": wallpaperLastPath,
|
||||||
"profileLastPath": profileLastPath
|
"profileLastPath": profileLastPath
|
||||||
@@ -439,23 +436,16 @@ gtk-application-prefer-dark-theme=true`;
|
|||||||
|
|
||||||
// Trigger dynamic theming if enabled
|
// Trigger dynamic theming if enabled
|
||||||
if (wallpaperDynamicTheming && path && typeof Theme !== "undefined") {
|
if (wallpaperDynamicTheming && path && typeof Theme !== "undefined") {
|
||||||
console.log("Wallpaper changed, triggering dynamic theme extraction");
|
|
||||||
Theme.switchTheme(themeIndex, true, true);
|
Theme.switchTheme(themeIndex, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperDirectory(directory) {
|
|
||||||
wallpaperDirectory = directory;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperDynamicTheming(enabled) {
|
function setWallpaperDynamicTheming(enabled) {
|
||||||
wallpaperDynamicTheming = enabled;
|
wallpaperDynamicTheming = enabled;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
// If enabled and we have a wallpaper, trigger dynamic theming
|
// If enabled and we have a wallpaper, trigger dynamic theming
|
||||||
if (enabled && wallpaperPath && typeof Theme !== "undefined") {
|
if (enabled && wallpaperPath && typeof Theme !== "undefined") {
|
||||||
console.log("Dynamic theming enabled, triggering theme extraction");
|
|
||||||
Theme.switchTheme(themeIndex, true, true);
|
Theme.switchTheme(themeIndex, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,11 +461,7 @@ gtk-application-prefer-dark-theme=true`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: loadSettings()
|
Component.onCompleted: loadSettings()
|
||||||
onShowSystemResourcesChanged: {
|
|
||||||
if (typeof SystemMonitorService !== 'undefined')
|
|
||||||
SystemMonitorService.enableTopBarMonitoring(showSystemResources);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: settingsFile
|
id: settingsFile
|
||||||
@@ -529,7 +515,7 @@ gtk-application-prefer-dark-theme=true`;
|
|||||||
id: qtThemeProcess
|
id: qtThemeProcess
|
||||||
running: false
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
console.log("Qt theme reload signal sent, exit code:", exitCode);
|
// Qt theme reload signal sent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,5 +573,31 @@ gtk-application-prefer-dark-theme=true`;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "wallpaper"
|
||||||
|
|
||||||
|
function get(): string {
|
||||||
|
return root.wallpaperPath || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(path: string): string {
|
||||||
|
if (!path) {
|
||||||
|
return "ERROR: No path provided"
|
||||||
|
}
|
||||||
|
|
||||||
|
var absolutePath = path.startsWith("/") ? path : StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/" + path
|
||||||
|
|
||||||
|
try {
|
||||||
|
root.setWallpaper(absolutePath)
|
||||||
|
return "SUCCESS: Wallpaper set to " + absolutePath
|
||||||
|
} catch (e) {
|
||||||
|
return "ERROR: Failed to set wallpaper: " + e.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(): string {
|
||||||
|
root.setWallpaper("")
|
||||||
|
return "SUCCESS: Wallpaper cleared"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,11 @@ DankModal {
|
|||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
processListModal.visible = true;
|
processListModal.visible = true;
|
||||||
SystemMonitorService.enableDetailedMonitoring(true);
|
|
||||||
SystemMonitorService.updateSystemInfo();
|
|
||||||
UserInfoService.getUptime();
|
UserInfoService.getUptime();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
processListModal.visible = false;
|
processListModal.visible = false;
|
||||||
SystemMonitorService.enableDetailedMonitoring(false);
|
|
||||||
// Close any open context menus
|
// Close any open context menus
|
||||||
if (processContextMenu.visible) {
|
if (processContextMenu.visible) {
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
@@ -49,7 +46,7 @@ DankModal {
|
|||||||
enableShadow: true
|
enableShadow: true
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: ProcessMonitorService
|
service: SysMonitorService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -100,7 +97,7 @@ DankModal {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: ProcessMonitorService.processes.length + " processes"
|
text: SysMonitorService.processes.length + " processes"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
width: Math.min(implicitWidth, 120)
|
width: Math.min(implicitWidth, 120)
|
||||||
@@ -221,6 +218,7 @@ DankModal {
|
|||||||
id: processesTab
|
id: processesTab
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
active: currentTab === 0
|
||||||
visible: currentTab === 0
|
visible: currentTab === 0
|
||||||
opacity: currentTab === 0 ? 1 : 0
|
opacity: currentTab === 0 ? 1 : 0
|
||||||
sourceComponent: processesTabComponent
|
sourceComponent: processesTabComponent
|
||||||
@@ -239,6 +237,7 @@ DankModal {
|
|||||||
id: performanceTab
|
id: performanceTab
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
active: currentTab === 1
|
||||||
visible: currentTab === 1
|
visible: currentTab === 1
|
||||||
opacity: currentTab === 1 ? 1 : 0
|
opacity: currentTab === 1 ? 1 : 0
|
||||||
sourceComponent: performanceTabComponent
|
sourceComponent: performanceTabComponent
|
||||||
@@ -257,6 +256,7 @@ DankModal {
|
|||||||
id: systemTab
|
id: systemTab
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
active: currentTab === 2
|
||||||
visible: currentTab === 2
|
visible: currentTab === 2
|
||||||
opacity: currentTab === 2 ? 1 : 0
|
opacity: currentTab === 2 ? 1 : 0
|
||||||
sourceComponent: systemTabComponent
|
sourceComponent: systemTabComponent
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -76,95 +78,90 @@ DankModal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings sections
|
// Tabbed Settings
|
||||||
ScrollView {
|
Column {
|
||||||
id: settingsScrollView
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 50
|
height: parent.height - 50
|
||||||
clip: true
|
spacing: 0
|
||||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|
||||||
|
|
||||||
|
DankTabBar {
|
||||||
|
id: settingsTabBar
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
model: [
|
||||||
|
{ text: "Personalization", icon: "person" },
|
||||||
|
{ text: "Time & Weather", icon: "schedule" },
|
||||||
|
{ text: "Widgets", icon: "widgets" },
|
||||||
|
{ text: "Appearance", icon: "palette" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - settingsTabBar.height
|
||||||
|
|
||||||
|
// Personalization Tab
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: settingsTabBar.currentIndex === 0
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: Component {
|
||||||
|
PersonalizationTab {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time & Weather Tab
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: settingsTabBar.currentIndex === 1
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: Component {
|
||||||
Column {
|
Column {
|
||||||
id: settingsColumn
|
|
||||||
|
|
||||||
width: settingsScrollView.width - 20
|
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
bottomPadding: Theme.spacingL
|
topPadding: Theme.spacingM
|
||||||
|
|
||||||
// Profile Settings
|
ClockTab {
|
||||||
SettingsSection {
|
width: parent.width
|
||||||
title: "Profile"
|
|
||||||
iconName: "person"
|
|
||||||
|
|
||||||
content: ProfileTab {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: Theme.spacingL
|
||||||
|
color: "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wallpaper Settings
|
WeatherTab {
|
||||||
SettingsSection {
|
width: parent.width
|
||||||
title: "Wallpaper"
|
}
|
||||||
iconName: "wallpaper"
|
}
|
||||||
|
}
|
||||||
content: WallpaperTab {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// System Tab
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: settingsTabBar.currentIndex === 2
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: Component {
|
||||||
|
SystemTab {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clock Settings
|
// Appearance Tab
|
||||||
SettingsSection {
|
Loader {
|
||||||
title: "Clock & Time"
|
anchors.fill: parent
|
||||||
iconName: "schedule"
|
active: settingsTabBar.currentIndex === 3
|
||||||
|
visible: active
|
||||||
content: ClockTab {
|
asynchronous: true
|
||||||
|
sourceComponent: Component {
|
||||||
|
AppearanceTab {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather Settings
|
|
||||||
SettingsSection {
|
|
||||||
title: "Weather"
|
|
||||||
iconName: "wb_sunny"
|
|
||||||
|
|
||||||
content: WeatherTab {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget Visibility Settings
|
|
||||||
SettingsSection {
|
|
||||||
title: "Top Bar Widgets"
|
|
||||||
iconName: "widgets"
|
|
||||||
|
|
||||||
content: WidgetsTab {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workspace Settings
|
|
||||||
SettingsSection {
|
|
||||||
title: "Workspaces"
|
|
||||||
iconName: "tab"
|
|
||||||
|
|
||||||
content: WorkspaceTab {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display Settings
|
|
||||||
SettingsSection {
|
|
||||||
title: "Display & Appearance"
|
|
||||||
iconName: "palette"
|
|
||||||
|
|
||||||
content: DisplayTab {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
smooth: true
|
smooth: true
|
||||||
|
cache: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -7,6 +7,18 @@ Column {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
// Trigger immediate updates for both services
|
||||||
|
SysMonitorService.updateSystemStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
function formatNetworkSpeed(bytesPerSec) {
|
function formatNetworkSpeed(bytesPerSec) {
|
||||||
if (bytesPerSec < 1024)
|
if (bytesPerSec < 1024)
|
||||||
return bytesPerSec.toFixed(0) + " B/s";
|
return bytesPerSec.toFixed(0) + " B/s";
|
||||||
@@ -61,7 +73,7 @@ Column {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalCpuUsage.toFixed(1) + "%"
|
text: SysMonitorService.totalCpuUsage.toFixed(1) + "%"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -76,7 +88,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.cpuCount + " cores"
|
text: SysMonitorService.cpuCount + " cores"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -96,7 +108,7 @@ Column {
|
|||||||
spacing: 6
|
spacing: 6
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: ProcessMonitorService.perCoreCpuUsage.length
|
model: SysMonitorService.perCoreCpuUsage.length
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -119,11 +131,11 @@ Column {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width * Math.min(1, ProcessMonitorService.perCoreCpuUsage[index] / 100)
|
width: parent.width * Math.min(1, SysMonitorService.perCoreCpuUsage[index] / 100)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: {
|
color: {
|
||||||
const usage = ProcessMonitorService.perCoreCpuUsage[index];
|
const usage = SysMonitorService.perCoreCpuUsage[index];
|
||||||
if (usage > 80)
|
if (usage > 80)
|
||||||
return Theme.error;
|
return Theme.error;
|
||||||
|
|
||||||
@@ -145,7 +157,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.perCoreCpuUsage[index] ? ProcessMonitorService.perCoreCpuUsage[index].toFixed(0) + "%" : "0%"
|
text: SysMonitorService.perCoreCpuUsage[index] ? SysMonitorService.perCoreCpuUsage[index].toFixed(0) + "%" : "0%"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -191,7 +203,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedMemoryKB) + " / " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalMemoryKB)
|
text: SysMonitorService.formatSystemMemory(SysMonitorService.usedMemoryKB) + " / " + SysMonitorService.formatSystemMemory(SysMonitorService.totalMemoryKB)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
@@ -215,11 +227,11 @@ Column {
|
|||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: ProcessMonitorService.totalMemoryKB > 0 ? parent.width * (ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) : 0
|
width: SysMonitorService.totalMemoryKB > 0 ? parent.width * (SysMonitorService.usedMemoryKB / SysMonitorService.totalMemoryKB) : 0
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: {
|
color: {
|
||||||
const usage = ProcessMonitorService.totalMemoryKB > 0 ? (ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) : 0;
|
const usage = SysMonitorService.totalMemoryKB > 0 ? (SysMonitorService.usedMemoryKB / SysMonitorService.totalMemoryKB) : 0;
|
||||||
if (usage > 0.9)
|
if (usage > 0.9)
|
||||||
return Theme.error;
|
return Theme.error;
|
||||||
|
|
||||||
@@ -241,7 +253,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalMemoryKB > 0 ? ((ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) * 100).toFixed(1) + "% used" : "No data"
|
text: SysMonitorService.totalMemoryKB > 0 ? ((SysMonitorService.usedMemoryKB / SysMonitorService.totalMemoryKB) * 100).toFixed(1) + "% used" : "No data"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -266,8 +278,8 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalSwapKB > 0 ?
|
text: SysMonitorService.totalSwapKB > 0 ?
|
||||||
ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedSwapKB) + " / " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalSwapKB) :
|
SysMonitorService.formatSystemMemory(SysMonitorService.usedSwapKB) + " / " + SysMonitorService.formatSystemMemory(SysMonitorService.totalSwapKB) :
|
||||||
"No swap configured"
|
"No swap configured"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
@@ -292,12 +304,12 @@ Column {
|
|||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: ProcessMonitorService.totalSwapKB > 0 ? parent.width * (ProcessMonitorService.usedSwapKB / ProcessMonitorService.totalSwapKB) : 0
|
width: SysMonitorService.totalSwapKB > 0 ? parent.width * (SysMonitorService.usedSwapKB / SysMonitorService.totalSwapKB) : 0
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: {
|
color: {
|
||||||
if (!ProcessMonitorService.totalSwapKB) return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3);
|
if (!SysMonitorService.totalSwapKB) return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3);
|
||||||
const usage = ProcessMonitorService.usedSwapKB / ProcessMonitorService.totalSwapKB;
|
const usage = SysMonitorService.usedSwapKB / SysMonitorService.totalSwapKB;
|
||||||
if (usage > 0.9) return Theme.error;
|
if (usage > 0.9) return Theme.error;
|
||||||
if (usage > 0.7) return Theme.warning;
|
if (usage > 0.7) return Theme.warning;
|
||||||
return Theme.info;
|
return Theme.info;
|
||||||
@@ -312,7 +324,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalSwapKB > 0 ? ((ProcessMonitorService.usedSwapKB / ProcessMonitorService.totalSwapKB) * 100).toFixed(1) + "% used" : "Not available"
|
text: SysMonitorService.totalSwapKB > 0 ? ((SysMonitorService.usedSwapKB / SysMonitorService.totalSwapKB) * 100).toFixed(1) + "% used" : "Not available"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -363,7 +375,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: formatNetworkSpeed(ProcessMonitorService.networkRxRate)
|
text: SysMonitorService.networkRxRate > 0 ? formatNetworkSpeed(SysMonitorService.networkRxRate) : "0 B/s"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -381,7 +393,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: formatNetworkSpeed(ProcessMonitorService.networkTxRate)
|
text: SysMonitorService.networkTxRate > 0 ? formatNetworkSpeed(SysMonitorService.networkTxRate) : "0 B/s"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -429,7 +441,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: formatDiskSpeed(ProcessMonitorService.diskReadRate)
|
text: formatDiskSpeed(SysMonitorService.diskReadRate)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -447,7 +459,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: formatDiskSpeed(ProcessMonitorService.diskWriteRate)
|
text: formatDiskSpeed(SysMonitorService.diskWriteRate)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Rectangle {
|
|||||||
DankIcon {
|
DankIcon {
|
||||||
id: processIcon
|
id: processIcon
|
||||||
|
|
||||||
name: ProcessMonitorService.getProcessIcon(process ? process.command : "")
|
name: SysMonitorService.getProcessIcon(process ? process.command : "")
|
||||||
size: Theme.iconSize - 4
|
size: Theme.iconSize - 4
|
||||||
color: {
|
color: {
|
||||||
if (process && process.cpu > 80)
|
if (process && process.cpu > 80)
|
||||||
@@ -97,7 +97,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.formatCpuUsage(process ? process.cpu : 0)
|
text: SysMonitorService.formatCpuUsage(process ? process.cpu : 0)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: {
|
color: {
|
||||||
@@ -134,7 +134,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.formatMemoryUsage(process ? process.memoryKB : 0)
|
text: SysMonitorService.formatMemoryUsage(process ? process.memoryKB : 0)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: {
|
color: {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ PanelWindow {
|
|||||||
visible: isVisible
|
visible: isVisible
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: ProcessMonitorService
|
service: SysMonitorService
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: 600
|
implicitWidth: 600
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ Column {
|
|||||||
id: root
|
id: root
|
||||||
property var contextMenu: null
|
property var contextMenu: null
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: columnHeaders
|
id: columnHeaders
|
||||||
|
|
||||||
@@ -14,66 +22,129 @@ Column {
|
|||||||
anchors.leftMargin: 8
|
anchors.leftMargin: 8
|
||||||
height: 24
|
height: 24
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 60
|
||||||
|
height: 20
|
||||||
|
color: processHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "Process"
|
text: "Process"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: SysMonitorService.sortBy === "name" ? Font.Bold : Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: SysMonitorService.sortBy === "name" ? 1.0 : 0.7
|
||||||
anchors.left: parent.left
|
anchors.centerIn: parent
|
||||||
anchors.leftMargin: 0 // Left align with content area
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
MouseArea {
|
||||||
|
id: processHeaderArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SysMonitorService.setSortBy("name")
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 80
|
width: 80
|
||||||
height: 20
|
height: 20
|
||||||
color: "transparent"
|
color: cpuHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 200 // Slight adjustment to move right
|
anchors.rightMargin: 200
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "CPU"
|
text: "CPU"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: SysMonitorService.sortBy === "cpu" ? Font.Bold : Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: SysMonitorService.sortBy === "cpu" ? 1.0 : 0.7
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: cpuHeaderArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SysMonitorService.setSortBy("cpu")
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 80
|
width: 80
|
||||||
height: 20
|
height: 20
|
||||||
color: "transparent"
|
color: memoryHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 112 // Move right by decreasing rightMargin
|
anchors.rightMargin: 112
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "RAM"
|
text: "RAM"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: SysMonitorService.sortBy === "memory" ? Font.Bold : Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: SysMonitorService.sortBy === "memory" ? 1.0 : 0.7
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: memoryHeaderArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SysMonitorService.setSortBy("memory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 50
|
||||||
|
height: 20
|
||||||
|
color: pidHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 53
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "PID"
|
text: "PID"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: SysMonitorService.sortBy === "pid" ? Font.Bold : Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: SysMonitorService.sortBy === "pid" ? 1.0 : 0.7
|
||||||
width: 50
|
horizontalAlignment: Text.AlignHCenter
|
||||||
horizontalAlignment: Text.AlignRight
|
anchors.centerIn: parent
|
||||||
anchors.right: parent.right
|
}
|
||||||
anchors.rightMargin: 53 // Move left by increasing rightMargin
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
MouseArea {
|
||||||
|
id: pidHeaderArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SysMonitorService.setSortBy("pid")
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: Theme.shortDuration }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -86,7 +157,7 @@ Column {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.sortDescending ? "↓" : "↑"
|
text: SysMonitorService.sortDescending ? "↓" : "↑"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -98,7 +169,7 @@ Column {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: ProcessMonitorService.toggleSortOrder()
|
onClicked: SysMonitorService.toggleSortOrder()
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -123,7 +194,7 @@ Column {
|
|||||||
id: processListView
|
id: processListView
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: ProcessMonitorService.processes
|
model: SysMonitorService.processes
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
delegate: ProcessListItem {
|
delegate: ProcessListItem {
|
||||||
|
|||||||
@@ -6,20 +6,28 @@ Row {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: (parent.width - Theme.spacingM * 2) / 3
|
width: (parent.width - Theme.spacingM * 2) / 3
|
||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: {
|
color: {
|
||||||
if (ProcessMonitorService.sortBy === "cpu")
|
if (SysMonitorService.sortBy === "cpu")
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
||||||
else if (cpuCardMouseArea.containsMouse)
|
else if (cpuCardMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
else
|
else
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||||
}
|
}
|
||||||
border.color: ProcessMonitorService.sortBy === "cpu" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
border.color: SysMonitorService.sortBy === "cpu" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||||
border.width: ProcessMonitorService.sortBy === "cpu" ? 2 : 1
|
border.width: SysMonitorService.sortBy === "cpu" ? 2 : 1
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cpuCardMouseArea
|
id: cpuCardMouseArea
|
||||||
@@ -27,7 +35,7 @@ Row {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: ProcessMonitorService.setSortBy("cpu")
|
onClicked: SysMonitorService.setSortBy("cpu")
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -40,19 +48,19 @@ Row {
|
|||||||
text: "CPU"
|
text: "CPU"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: ProcessMonitorService.sortBy === "cpu" ? Theme.primary : Theme.secondary
|
color: SysMonitorService.sortBy === "cpu" ? Theme.primary : Theme.secondary
|
||||||
opacity: ProcessMonitorService.sortBy === "cpu" ? 1 : 0.8
|
opacity: SysMonitorService.sortBy === "cpu" ? 1 : 0.8
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalCpuUsage.toFixed(1) + "%"
|
text: SysMonitorService.totalCpuUsage.toFixed(1) + "%"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.cpuCount + " cores"
|
text: SysMonitorService.cpuCount + " cores"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
@@ -81,15 +89,15 @@ Row {
|
|||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: {
|
color: {
|
||||||
if (ProcessMonitorService.sortBy === "memory")
|
if (SysMonitorService.sortBy === "memory")
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
||||||
else if (memoryCardMouseArea.containsMouse)
|
else if (memoryCardMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.12);
|
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.12);
|
||||||
else
|
else
|
||||||
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08);
|
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08);
|
||||||
}
|
}
|
||||||
border.color: ProcessMonitorService.sortBy === "memory" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.2)
|
border.color: SysMonitorService.sortBy === "memory" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.2)
|
||||||
border.width: ProcessMonitorService.sortBy === "memory" ? 2 : 1
|
border.width: SysMonitorService.sortBy === "memory" ? 2 : 1
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: memoryCardMouseArea
|
id: memoryCardMouseArea
|
||||||
@@ -97,7 +105,7 @@ Row {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: ProcessMonitorService.setSortBy("memory")
|
onClicked: SysMonitorService.setSortBy("memory")
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -110,19 +118,19 @@ Row {
|
|||||||
text: "Memory"
|
text: "Memory"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: ProcessMonitorService.sortBy === "memory" ? Theme.primary : Theme.secondary
|
color: SysMonitorService.sortBy === "memory" ? Theme.primary : Theme.secondary
|
||||||
opacity: ProcessMonitorService.sortBy === "memory" ? 1 : 0.8
|
opacity: SysMonitorService.sortBy === "memory" ? 1 : 0.8
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedMemoryKB)
|
text: SysMonitorService.formatSystemMemory(SysMonitorService.usedMemoryKB)
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "of " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalMemoryKB)
|
text: "of " + SysMonitorService.formatSystemMemory(SysMonitorService.totalMemoryKB)
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
@@ -150,8 +158,8 @@ Row {
|
|||||||
width: (parent.width - Theme.spacingM * 2) / 3
|
width: (parent.width - Theme.spacingM * 2) / 3
|
||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: ProcessMonitorService.totalSwapKB > 0 ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.08) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.04)
|
color: SysMonitorService.totalSwapKB > 0 ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.08) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.04)
|
||||||
border.color: ProcessMonitorService.totalSwapKB > 0 ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.2) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12)
|
border.color: SysMonitorService.totalSwapKB > 0 ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.2) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -164,19 +172,19 @@ Row {
|
|||||||
text: "Swap"
|
text: "Swap"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: ProcessMonitorService.totalSwapKB > 0 ? Theme.warning : Theme.surfaceText
|
color: SysMonitorService.totalSwapKB > 0 ? Theme.warning : Theme.surfaceText
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalSwapKB > 0 ? ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedSwapKB) : "None"
|
text: SysMonitorService.totalSwapKB > 0 ? SysMonitorService.formatSystemMemory(SysMonitorService.usedSwapKB) : "None"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: ProcessMonitorService.totalSwapKB > 0 ? "of " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalSwapKB) : "No swap configured"
|
text: SysMonitorService.totalSwapKB > 0 ? "of " + SysMonitorService.formatSystemMemory(SysMonitorService.totalSwapKB) : "No swap configured"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ ScrollView {
|
|||||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -44,26 +52,26 @@ ScrollView {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: SystemMonitorService.hostname
|
text: SysMonitorService.hostname
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
font.weight: Font.Light
|
font.weight: Font.Light
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: SystemMonitorService.distribution + " • " + SystemMonitorService.architecture + " • " + SystemMonitorService.kernelVersion
|
text: SysMonitorService.distribution + " • " + SysMonitorService.architecture + " • " + SysMonitorService.kernelVersion
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "Up " + UserInfoService.uptime + " • Boot: " + SystemMonitorService.bootTime
|
text: "Up " + UserInfoService.uptime + " • Boot: " + SysMonitorService.bootTime
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "Load: " + SystemMonitorService.loadAverage + " • " + SystemMonitorService.processCount + " processes, " + SystemMonitorService.threadCount + " threads"
|
text: "Load: " + SysMonitorService.loadAverage + " • " + SysMonitorService.processCount + " processes, " + SysMonitorService.threadCount + " threads"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
}
|
}
|
||||||
@@ -87,7 +95,7 @@ ScrollView {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: SystemMonitorService.cpuModel
|
text: SysMonitorService.cpuModel
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -96,7 +104,7 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: SystemMonitorService.motherboard
|
text: SysMonitorService.motherboard
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -111,7 +119,7 @@ ScrollView {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: SystemMonitorService.formatMemory(SystemMonitorService.totalMemory) + " Memory"
|
text: SysMonitorService.formatMemory(SysMonitorService.totalMemoryMB) + " Memory"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -120,7 +128,7 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "BIOS " + SystemMonitorService.biosVersion
|
text: "BIOS " + SysMonitorService.biosVersion
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
@@ -173,13 +181,6 @@ ScrollView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
text: "I/O Scheduler: " + SystemMonitorService.scheduler
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -249,7 +250,7 @@ ScrollView {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: diskMountRepeater
|
id: diskMountRepeater
|
||||||
|
|
||||||
model: SystemMonitorService.diskMounts
|
model: SysMonitorService.diskMounts
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
56
Modules/Settings/AppearanceTab.qml
Normal file
56
Modules/Settings/AppearanceTab.qml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: root.width - 20
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
topPadding: Theme.spacingM
|
||||||
|
bottomPadding: Theme.spacingL
|
||||||
|
|
||||||
|
// Display Settings
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "monitor"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Display"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayTab {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
Modules/Settings/PersonalizationTab.qml
Normal file
91
Modules/Settings/PersonalizationTab.qml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: root.width - 20
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
topPadding: Theme.spacingM
|
||||||
|
bottomPadding: Theme.spacingL
|
||||||
|
|
||||||
|
// Profile Section
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "person"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Profile"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileTab {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallpaper Section
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "wallpaper"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Wallpaper"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperTab {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,21 +43,21 @@ Column {
|
|||||||
visible: parent.hasImage
|
visible: parent.hasImage
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
CachingImage {
|
||||||
id: avatarImageSource
|
id: avatarImageSource
|
||||||
|
|
||||||
source: {
|
imagePath: {
|
||||||
if (Prefs.profileImage === "")
|
if (Prefs.profileImage === "")
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
if (Prefs.profileImage.startsWith("/"))
|
if (Prefs.profileImage.startsWith("/"))
|
||||||
return "file://" + Prefs.profileImage;
|
return Prefs.profileImage;
|
||||||
|
|
||||||
return Prefs.profileImage;
|
return Prefs.profileImage;
|
||||||
}
|
}
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
mipmap: true
|
maxCacheSize: 80
|
||||||
cache: true
|
cache: true
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ Column {
|
|||||||
id: profileBrowserLoader
|
id: profileBrowserLoader
|
||||||
active: false
|
active: false
|
||||||
|
|
||||||
FileBrowser {
|
DankFileBrowser {
|
||||||
id: profileBrowser
|
id: profileBrowser
|
||||||
browserTitle: "Select Profile Image"
|
browserTitle: "Select Profile Image"
|
||||||
browserIcon: "person"
|
browserIcon: "person"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
@@ -8,30 +10,71 @@ Column {
|
|||||||
property string title: ""
|
property string title: ""
|
||||||
property string iconName: ""
|
property string iconName: ""
|
||||||
property alias content: contentLoader.sourceComponent
|
property alias content: contentLoader.sourceComponent
|
||||||
|
property bool expanded: false
|
||||||
|
property bool collapsible: true
|
||||||
|
property bool lazyLoad: true
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingM
|
spacing: expanded ? Theme.spacingM : 0
|
||||||
|
|
||||||
// Section header
|
// Section header
|
||||||
|
MouseArea {
|
||||||
|
width: parent.width
|
||||||
|
height: headerRow.height
|
||||||
|
enabled: collapsible
|
||||||
|
hoverEnabled: collapsible
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: parent.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.radiusS
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
id: headerRow
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
topPadding: Theme.spacingS
|
||||||
|
bottomPadding: Theme.spacingS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: iconName
|
name: root.collapsible ? (root.expanded ? "expand_less" : "expand_more") : root.iconName
|
||||||
size: Theme.iconSize - 2
|
size: Theme.iconSize - 2
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Appearance.anim.durations.fast
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: root.iconName
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.collapsible
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: title
|
text: root.title
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (collapsible) {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divider
|
// Divider
|
||||||
@@ -39,6 +82,7 @@ Column {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 1
|
height: 1
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
visible: expanded || !collapsible
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
@@ -46,6 +90,24 @@ Column {
|
|||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
active: lazyLoad ? expanded || !collapsible : true
|
||||||
|
visible: expanded || !collapsible
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (!collapsible) {
|
||||||
|
expanded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
91
Modules/Settings/SystemTab.qml
Normal file
91
Modules/Settings/SystemTab.qml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: root.width - 20
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
topPadding: Theme.spacingM
|
||||||
|
bottomPadding: Theme.spacingL
|
||||||
|
|
||||||
|
// Top Bar Widgets
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "widgets"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Top Bar Widgets"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetsTab {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspaces
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "tab"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Workspaces"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceTab {
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -270,9 +270,15 @@ Column {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (ToastService.wallpaperErrorStatus === "matugen_missing") {
|
||||||
|
ToastService.showError("matugen not found - install matugen package for dynamic theming");
|
||||||
|
} else if (ToastService.wallpaperErrorStatus === "error") {
|
||||||
|
ToastService.showError("Wallpaper processing failed - check wallpaper path");
|
||||||
|
} else {
|
||||||
Theme.switchTheme(10, true);
|
Theme.switchTheme(10, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tooltip for Auto button
|
// Tooltip for Auto button
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -36,12 +37,13 @@ Column {
|
|||||||
border.color: Theme.outline
|
border.color: Theme.outline
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Image {
|
CachingImage {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 1
|
anchors.margins: 1
|
||||||
source: Prefs.wallpaperPath ? "file://" + Prefs.wallpaperPath : ""
|
imagePath: Prefs.wallpaperPath || ""
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
visible: Prefs.wallpaperPath !== ""
|
visible: Prefs.wallpaperPath !== ""
|
||||||
|
maxCacheSize: 120
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
layer.effect: MultiEffect {
|
||||||
maskEnabled: true
|
maskEnabled: true
|
||||||
@@ -204,21 +206,32 @@ Column {
|
|||||||
DankToggle {
|
DankToggle {
|
||||||
id: toggle
|
id: toggle
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
checked: Prefs.wallpaperDynamicTheming
|
checked: Theme.isDynamicTheme
|
||||||
onCheckedChanged: {
|
enabled: ToastService.wallpaperErrorStatus !== "matugen_missing"
|
||||||
if (activeFocus) {
|
onToggled: (toggled) => {
|
||||||
Prefs.setWallpaperDynamicTheming(checked);
|
if (toggled) {
|
||||||
|
Theme.switchTheme(10, true)
|
||||||
|
} else {
|
||||||
|
Theme.switchTheme(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "matugen not detected"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
visible: ToastService.wallpaperErrorStatus === "matugen_missing"
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: wallpaperBrowserLoader
|
id: wallpaperBrowserLoader
|
||||||
active: false
|
active: false
|
||||||
|
|
||||||
FileBrowser {
|
DankFileBrowser {
|
||||||
id: wallpaperBrowser
|
id: wallpaperBrowser
|
||||||
browserTitle: "Select Wallpaper"
|
browserTitle: "Select Wallpaper"
|
||||||
browserIcon: "wallpaper"
|
browserIcon: "wallpaper"
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ Item {
|
|||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
root.cavaAvailable = exitCode === 0;
|
root.cavaAvailable = exitCode === 0;
|
||||||
if (root.cavaAvailable) {
|
if (root.cavaAvailable) {
|
||||||
console.log("cava found - enabling real audio visualization");
|
|
||||||
cavaProcess.running = Qt.binding(() => {
|
cavaProcess.running = Qt.binding(() => {
|
||||||
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("cava not found - using fallback animation");
|
|
||||||
fallbackTimer.running = Qt.binding(() => {
|
fallbackTimer.running = Qt.binding(() => {
|
||||||
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: cpuArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
color: cpuArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cpuArea
|
id: cpuArea
|
||||||
|
|
||||||
@@ -24,7 +32,7 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ProcessMonitorService.setSortBy("cpu");
|
SysMonitorService.setSortBy("cpu");
|
||||||
if (root.toggleProcessList)
|
if (root.toggleProcessList)
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
@@ -38,10 +46,10 @@ Rectangle {
|
|||||||
name: "memory"
|
name: "memory"
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
color: {
|
color: {
|
||||||
if (SystemMonitorService.cpuUsage > 80)
|
if (SysMonitorService.cpuUsage > 80)
|
||||||
return Theme.error;
|
return Theme.error;
|
||||||
|
|
||||||
if (SystemMonitorService.cpuUsage > 60)
|
if (SysMonitorService.cpuUsage > 60)
|
||||||
return Theme.warning;
|
return Theme.warning;
|
||||||
|
|
||||||
return Theme.surfaceText;
|
return Theme.surfaceText;
|
||||||
@@ -50,7 +58,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: (SystemMonitorService.cpuUsage || 0).toFixed(0) + "%"
|
text: (SysMonitorService.cpuUsage || 0).toFixed(0) + "%"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ Rectangle {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: ramArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
color: ramArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
SysMonitorService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
SysMonitorService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: ramArea
|
id: ramArea
|
||||||
|
|
||||||
@@ -24,7 +32,7 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ProcessMonitorService.setSortBy("memory");
|
SysMonitorService.setSortBy("memory");
|
||||||
if (root.toggleProcessList)
|
if (root.toggleProcessList)
|
||||||
root.toggleProcessList();
|
root.toggleProcessList();
|
||||||
}
|
}
|
||||||
@@ -38,10 +46,10 @@ Rectangle {
|
|||||||
name: "developer_board"
|
name: "developer_board"
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
color: {
|
color: {
|
||||||
if (SystemMonitorService.memoryUsage > 90)
|
if (SysMonitorService.memoryUsage > 90)
|
||||||
return Theme.error;
|
return Theme.error;
|
||||||
|
|
||||||
if (SystemMonitorService.memoryUsage > 75)
|
if (SysMonitorService.memoryUsage > 75)
|
||||||
return Theme.warning;
|
return Theme.warning;
|
||||||
|
|
||||||
return Theme.surfaceText;
|
return Theme.surfaceText;
|
||||||
@@ -50,7 +58,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: (SystemMonitorService.memoryUsage || 0).toFixed(0) + "%"
|
text: (SysMonitorService.memoryUsage || 0).toFixed(0) + "%"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
|||||||
@@ -22,19 +22,9 @@ Rectangle {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: SystemTray.items
|
model: SystemTray.items
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Item {
|
||||||
property var trayItem: modelData
|
property var trayItem: modelData
|
||||||
|
property string iconSource: {
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: Theme.cornerRadiusSmall
|
|
||||||
color: trayItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 18
|
|
||||||
height: 18
|
|
||||||
source: {
|
|
||||||
let icon = trayItem && trayItem.icon;
|
let icon = trayItem && trayItem.icon;
|
||||||
if (typeof icon === 'string' || icon instanceof String) {
|
if (typeof icon === 'string' || icon instanceof String) {
|
||||||
if (icon.includes("?path=")) {
|
if (icon.includes("?path=")) {
|
||||||
@@ -44,8 +34,31 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
return ""; // Return empty string if icon is not a string
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.cornerRadiusSmall
|
||||||
|
color: trayItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
enabled: trayItemArea.containsMouse !== undefined
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 18
|
||||||
|
height: 18
|
||||||
|
source: parent.iconSource
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
smooth: true
|
smooth: true
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
@@ -53,51 +66,23 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: trayItemArea
|
id: trayItemArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
if (!trayItem)
|
if (!trayItem) return;
|
||||||
return ;
|
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
if (!trayItem.onlyMenu)
|
if (!trayItem.onlyMenu)
|
||||||
trayItem.activate();
|
trayItem.activate();
|
||||||
|
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
if (trayItem && trayItem.hasMenu)
|
if (trayItem && trayItem.hasMenu) {
|
||||||
customTrayMenu.showMenu(mouse.x, mouse.y);
|
root.menuRequested(null, trayItem, mouse.x, mouse.y);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: customTrayMenu
|
|
||||||
|
|
||||||
property bool menuVisible: false
|
|
||||||
|
|
||||||
function showMenu(x, y) {
|
|
||||||
root.menuRequested(customTrayMenu, trayItem, x, y);
|
|
||||||
menuVisible = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideMenu() {
|
|
||||||
menuVisible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,17 +234,25 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CpuMonitor {
|
Loader {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: Prefs.showSystemResources
|
active: Prefs.showSystemResources
|
||||||
|
sourceComponent: Component {
|
||||||
|
CpuMonitor {
|
||||||
toggleProcessList: () => processListPopout.toggle()
|
toggleProcessList: () => processListPopout.toggle()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RamMonitor {
|
Loader {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: Prefs.showSystemResources
|
active: Prefs.showSystemResources
|
||||||
|
sourceComponent: Component {
|
||||||
|
RamMonitor {
|
||||||
toggleProcessList: () => processListPopout.toggle()
|
toggleProcessList: () => processListPopout.toggle()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NotificationCenterButton {
|
NotificationCenterButton {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -5,59 +5,133 @@ import Quickshell.Widgets
|
|||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Variants {
|
LazyLoader {
|
||||||
|
active: Prefs.wallpaperPath !== ""
|
||||||
|
|
||||||
|
Variants {
|
||||||
model: Quickshell.screens
|
model: Quickshell.screens
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: wallpaperWindow
|
id: wallpaperWindow
|
||||||
|
|
||||||
property var modelData
|
required property var modelData
|
||||||
|
|
||||||
screen: modelData
|
screen: modelData
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayer.Background
|
WlrLayershell.layer: WlrLayer.Background
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.bottom: true
|
anchors.bottom: true
|
||||||
anchors.left: true
|
anchors.left: true
|
||||||
anchors.right: true
|
anchors.right: true
|
||||||
|
|
||||||
visible: true
|
color: "black"
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
Image {
|
Item {
|
||||||
id: wallpaperImage
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property string source: Prefs.wallpaperPath || ""
|
||||||
|
property Image current: one
|
||||||
|
|
||||||
|
onSourceChanged: {
|
||||||
|
if (!source)
|
||||||
|
current = null;
|
||||||
|
else if (current === one)
|
||||||
|
two.update();
|
||||||
|
else
|
||||||
|
one.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: !root.source
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceComponent: Rectangle {
|
||||||
|
color: Theme.surface
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "sentiment_stressed"
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
size: Theme.iconSize * 5
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Wallpaper missing?"
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
font.pixelSize: Theme.fontSizeXLarge * 2
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Set wallpaper in Settings"
|
||||||
|
color: Theme.primary
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Img {
|
||||||
|
id: one
|
||||||
|
}
|
||||||
|
|
||||||
|
Img {
|
||||||
|
id: two
|
||||||
|
}
|
||||||
|
|
||||||
|
component Img: Image {
|
||||||
|
id: img
|
||||||
|
|
||||||
|
function update(): void {
|
||||||
|
source = "";
|
||||||
|
source = root.source;
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: Prefs.wallpaperPath ? "file://" + Prefs.wallpaperPath : ""
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
visible: Prefs.wallpaperPath !== ""
|
|
||||||
smooth: true
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
// Smooth transition when wallpaper changes
|
opacity: 0
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.anim.durations.normal
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === Image.Error) {
|
if (status === Image.Ready)
|
||||||
console.warn("Failed to load wallpaper:", source);
|
root.current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "visible"
|
||||||
|
when: root.current === img
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
img.opacity: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback background color when no wallpaper is set
|
transitions: Transition {
|
||||||
StyledRect {
|
NumberAnimation {
|
||||||
anchors.fill: parent
|
target: img
|
||||||
color: Theme.surface
|
properties: "opacity"
|
||||||
visible: !wallpaperImage.visible
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,6 @@ qs -c DankMaterialShell ipc call <target> <function>
|
|||||||
| processlist | toggle | Toggle process list (task manager) |
|
| processlist | toggle | Toggle process list (task manager) |
|
||||||
| wallpaper | set \<path\> | Set wallpaper to image path and refresh theme (if auto theme enabled) |
|
| wallpaper | set \<path\> | Set wallpaper to image path and refresh theme (if auto theme enabled) |
|
||||||
| wallpaper | get | Get current wallpaper path |
|
| wallpaper | get | Get current wallpaper path |
|
||||||
| wallpaper | list | List available wallpapers |
|
|
||||||
|
|
||||||
## (Optional) Setup Calendar events (Google, Microsoft, other Caldev, etc.)
|
## (Optional) Setup Calendar events (Google, Microsoft, other Caldev, etc.)
|
||||||
|
|
||||||
|
|||||||
@@ -1,411 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int refCount
|
|
||||||
|
|
||||||
property var processes: []
|
|
||||||
property bool isUpdating: false
|
|
||||||
property int processUpdateInterval: 3000
|
|
||||||
property int totalMemoryKB: 0
|
|
||||||
property int usedMemoryKB: 0
|
|
||||||
property int totalSwapKB: 0
|
|
||||||
property int usedSwapKB: 0
|
|
||||||
property int cpuCount: 1
|
|
||||||
property real totalCpuUsage: 0
|
|
||||||
property bool systemInfoAvailable: false
|
|
||||||
property var cpuHistory: []
|
|
||||||
property var memoryHistory: []
|
|
||||||
property var networkHistory: ({
|
|
||||||
"rx": [],
|
|
||||||
"tx": []
|
|
||||||
})
|
|
||||||
property var diskHistory: ({
|
|
||||||
"read": [],
|
|
||||||
"write": []
|
|
||||||
})
|
|
||||||
property int historySize: 60
|
|
||||||
property var perCoreCpuUsage: []
|
|
||||||
property real networkRxRate: 0
|
|
||||||
property real networkTxRate: 0
|
|
||||||
property var lastNetworkStats: null
|
|
||||||
property real diskReadRate: 0
|
|
||||||
property real diskWriteRate: 0
|
|
||||||
property var lastDiskStats: null
|
|
||||||
property string sortBy: "cpu"
|
|
||||||
property bool sortDescending: true
|
|
||||||
property int maxProcesses: 20
|
|
||||||
|
|
||||||
function updateProcessList() {
|
|
||||||
if (!root.isUpdating) {
|
|
||||||
root.isUpdating = true;
|
|
||||||
let sortOption = "";
|
|
||||||
switch (root.sortBy) {
|
|
||||||
case "cpu":
|
|
||||||
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu";
|
|
||||||
break;
|
|
||||||
case "memory":
|
|
||||||
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem";
|
|
||||||
break;
|
|
||||||
case "name":
|
|
||||||
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm";
|
|
||||||
break;
|
|
||||||
case "pid":
|
|
||||||
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sortOption = "--sort=-pcpu";
|
|
||||||
}
|
|
||||||
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)];
|
|
||||||
processListProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSortBy(newSortBy) {
|
|
||||||
if (newSortBy !== root.sortBy) {
|
|
||||||
root.sortBy = newSortBy;
|
|
||||||
updateProcessList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSortOrder() {
|
|
||||||
root.sortDescending = !root.sortDescending;
|
|
||||||
updateProcessList();
|
|
||||||
}
|
|
||||||
|
|
||||||
property int killPid: 0
|
|
||||||
|
|
||||||
function killProcess(pid) {
|
|
||||||
if (pid > 0) {
|
|
||||||
root.killPid = pid
|
|
||||||
processKiller.command = ["bash", "-c", "kill " + pid]
|
|
||||||
processKiller.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProcessIcon(command) {
|
|
||||||
const cmd = command.toLowerCase();
|
|
||||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
|
|
||||||
return "web";
|
|
||||||
|
|
||||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
|
|
||||||
return "code";
|
|
||||||
|
|
||||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
|
|
||||||
return "terminal";
|
|
||||||
|
|
||||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
|
|
||||||
return "music_note";
|
|
||||||
|
|
||||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
|
|
||||||
return "play_circle";
|
|
||||||
|
|
||||||
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
|
|
||||||
return "settings";
|
|
||||||
|
|
||||||
return "memory";
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatCpuUsage(cpu) {
|
|
||||||
return cpu.toFixed(1) + "%";
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatMemoryUsage(memoryKB) {
|
|
||||||
if (memoryKB < 1024)
|
|
||||||
return memoryKB.toFixed(0) + " KB";
|
|
||||||
else if (memoryKB < 1024 * 1024)
|
|
||||||
return (memoryKB / 1024).toFixed(1) + " MB";
|
|
||||||
else
|
|
||||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSystemMemory(memoryKB) {
|
|
||||||
if (memoryKB < 1024 * 1024)
|
|
||||||
return (memoryKB / 1024).toFixed(0) + " MB";
|
|
||||||
else
|
|
||||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseSystemInfo(text) {
|
|
||||||
const lines = text.split('\n');
|
|
||||||
let section = 'memory';
|
|
||||||
const coreUsages = [];
|
|
||||||
let memFree = 0;
|
|
||||||
let memBuffers = 0;
|
|
||||||
let memCached = 0;
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
const line = lines[i].trim();
|
|
||||||
if (line === '---CPU---') {
|
|
||||||
section = 'cpucount';
|
|
||||||
continue;
|
|
||||||
} else if (line === '---CPUSTAT---') {
|
|
||||||
section = 'cpustat';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (section === 'memory') {
|
|
||||||
if (line.startsWith('MemTotal:')) {
|
|
||||||
root.totalMemoryKB = parseInt(line.split(/\s+/)[1]);
|
|
||||||
} else if (line.startsWith('MemFree:')) {
|
|
||||||
memFree = parseInt(line.split(/\s+/)[1]);
|
|
||||||
} else if (line.startsWith('Buffers:')) {
|
|
||||||
memBuffers = parseInt(line.split(/\s+/)[1]);
|
|
||||||
} else if (line.startsWith('Cached:')) {
|
|
||||||
memCached = parseInt(line.split(/\s+/)[1]);
|
|
||||||
} else if (line.startsWith('SwapTotal:')) {
|
|
||||||
root.totalSwapKB = parseInt(line.split(/\s+/)[1]);
|
|
||||||
} else if (line.startsWith('SwapFree:')) {
|
|
||||||
const freeSwapKB = parseInt(line.split(/\s+/)[1]);
|
|
||||||
root.usedSwapKB = root.totalSwapKB - freeSwapKB;
|
|
||||||
}
|
|
||||||
} else if (section === 'cpucount') {
|
|
||||||
const count = parseInt(line);
|
|
||||||
if (!isNaN(count))
|
|
||||||
root.cpuCount = count;
|
|
||||||
|
|
||||||
} else if (section === 'cpustat') {
|
|
||||||
if (line.startsWith('cpu ')) {
|
|
||||||
const parts = line.split(/\s+/);
|
|
||||||
if (parts.length >= 8) {
|
|
||||||
const user = parseInt(parts[1]);
|
|
||||||
const nice = parseInt(parts[2]);
|
|
||||||
const system = parseInt(parts[3]);
|
|
||||||
const idle = parseInt(parts[4]);
|
|
||||||
const iowait = parseInt(parts[5]);
|
|
||||||
const irq = parseInt(parts[6]);
|
|
||||||
const softirq = parseInt(parts[7]);
|
|
||||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
|
||||||
const used = total - idle - iowait;
|
|
||||||
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0;
|
|
||||||
}
|
|
||||||
} else if (line.match(/^cpu\d+/)) {
|
|
||||||
const parts = line.split(/\s+/);
|
|
||||||
if (parts.length >= 8) {
|
|
||||||
const user = parseInt(parts[1]);
|
|
||||||
const nice = parseInt(parts[2]);
|
|
||||||
const system = parseInt(parts[3]);
|
|
||||||
const idle = parseInt(parts[4]);
|
|
||||||
const iowait = parseInt(parts[5]);
|
|
||||||
const irq = parseInt(parts[6]);
|
|
||||||
const softirq = parseInt(parts[7]);
|
|
||||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
|
||||||
const used = total - idle - iowait;
|
|
||||||
const usage = total > 0 ? (used / total) * 100 : 0;
|
|
||||||
coreUsages.push(usage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Calculate used memory as total minus free minus buffers minus cached
|
|
||||||
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached;
|
|
||||||
// Update per-core usage
|
|
||||||
root.perCoreCpuUsage = coreUsages;
|
|
||||||
// Update history
|
|
||||||
addToHistory(root.cpuHistory, root.totalCpuUsage);
|
|
||||||
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0;
|
|
||||||
addToHistory(root.memoryHistory, memoryPercent);
|
|
||||||
root.systemInfoAvailable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseNetworkStats(text) {
|
|
||||||
const lines = text.split('\n');
|
|
||||||
let totalRx = 0;
|
|
||||||
let totalTx = 0;
|
|
||||||
for (const line of lines) {
|
|
||||||
const parts = line.trim().split(/\s+/);
|
|
||||||
if (parts.length >= 3) {
|
|
||||||
const rx = parseInt(parts[1]);
|
|
||||||
const tx = parseInt(parts[2]);
|
|
||||||
if (!isNaN(rx) && !isNaN(tx)) {
|
|
||||||
totalRx += rx;
|
|
||||||
totalTx += tx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (root.lastNetworkStats) {
|
|
||||||
const timeDiff = root.processUpdateInterval / 1000;
|
|
||||||
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff);
|
|
||||||
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff);
|
|
||||||
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024);
|
|
||||||
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024);
|
|
||||||
}
|
|
||||||
root.lastNetworkStats = {
|
|
||||||
"rx": totalRx,
|
|
||||||
"tx": totalTx
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseDiskStats(text) {
|
|
||||||
const lines = text.split('\n');
|
|
||||||
let totalRead = 0;
|
|
||||||
let totalWrite = 0;
|
|
||||||
for (const line of lines) {
|
|
||||||
const parts = line.trim().split(/\s+/);
|
|
||||||
if (parts.length >= 3) {
|
|
||||||
const readSectors = parseInt(parts[1]);
|
|
||||||
const writeSectors = parseInt(parts[2]);
|
|
||||||
if (!isNaN(readSectors) && !isNaN(writeSectors)) {
|
|
||||||
totalRead += readSectors * 512;
|
|
||||||
totalWrite += writeSectors * 512;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (root.lastDiskStats) {
|
|
||||||
const timeDiff = root.processUpdateInterval / 1000;
|
|
||||||
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff);
|
|
||||||
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff);
|
|
||||||
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024));
|
|
||||||
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024));
|
|
||||||
}
|
|
||||||
root.lastDiskStats = {
|
|
||||||
"read": totalRead,
|
|
||||||
"write": totalWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addToHistory(array, value) {
|
|
||||||
array.push(value);
|
|
||||||
if (array.length > root.historySize)
|
|
||||||
array.shift();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
running: root.refCount > 0
|
|
||||||
interval: root.processUpdateInterval
|
|
||||||
repeat: true
|
|
||||||
triggeredOnStart: true
|
|
||||||
onTriggered: {
|
|
||||||
systemInfoProcess.running = true;
|
|
||||||
updateProcessList();
|
|
||||||
networkStatsProcess.running = true;
|
|
||||||
diskStatsProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: systemInfoProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
console.warn("System info check failed with exit code:", exitCode);
|
|
||||||
root.systemInfoAvailable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
parseSystemInfo(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: networkStatsProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
parseNetworkStats(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: diskStatsProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
parseDiskStats(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: processListProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
root.isUpdating = false;
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Process list check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim()) {
|
|
||||||
const lines = text.trim().split('\n');
|
|
||||||
const newProcesses = [];
|
|
||||||
for (let i = 1; i < lines.length; i++) {
|
|
||||||
const line = lines[i].trim();
|
|
||||||
if (!line)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const parts = line.split(/\s+/);
|
|
||||||
if (parts.length >= 7) {
|
|
||||||
const pid = parseInt(parts[0]);
|
|
||||||
const ppid = parseInt(parts[1]);
|
|
||||||
const cpu = parseFloat(parts[2]);
|
|
||||||
const memoryPercent = parseFloat(parts[3]);
|
|
||||||
const memoryKB = parseInt(parts[4]);
|
|
||||||
const command = parts[5];
|
|
||||||
const fullCmd = parts.slice(6).join(' ');
|
|
||||||
newProcesses.push({
|
|
||||||
"pid": pid,
|
|
||||||
"ppid": ppid,
|
|
||||||
"cpu": cpu,
|
|
||||||
"memoryPercent": memoryPercent,
|
|
||||||
"memoryKB": memoryKB,
|
|
||||||
"command": command,
|
|
||||||
"fullCommand": fullCmd,
|
|
||||||
"displayName": command.length > 15 ? command.substring(0, 15) + "..." : command
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root.processes = newProcesses;
|
|
||||||
root.isUpdating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: processKiller
|
|
||||||
command: ["bash", "-c", "kill " + root.killPid]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("Process killed successfully:", root.killPid)
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to kill process:", root.killPid, "exit code:", exitCode)
|
|
||||||
}
|
|
||||||
root.killPid = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
578
Services/SysMonitorService.qml
Normal file
578
Services/SysMonitorService.qml
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int refCount: 0
|
||||||
|
property int updateInterval: 8000
|
||||||
|
property int maxProcesses: 100
|
||||||
|
property bool isUpdating: false
|
||||||
|
|
||||||
|
// Process data
|
||||||
|
property var processes: []
|
||||||
|
property string sortBy: "cpu"
|
||||||
|
property bool sortDescending: true
|
||||||
|
|
||||||
|
// System stats
|
||||||
|
property real cpuUsage: 0
|
||||||
|
property real totalCpuUsage: 0
|
||||||
|
property int cpuCores: 1
|
||||||
|
property int cpuCount: 1
|
||||||
|
property string cpuModel: ""
|
||||||
|
property real cpuFrequency: 0
|
||||||
|
property real cpuTemperature: 0
|
||||||
|
property var perCoreCpuUsage: []
|
||||||
|
|
||||||
|
// Memory stats
|
||||||
|
property real memoryUsage: 0
|
||||||
|
property real totalMemoryMB: 0
|
||||||
|
property real usedMemoryMB: 0
|
||||||
|
property real freeMemoryMB: 0
|
||||||
|
property real availableMemoryMB: 0
|
||||||
|
property int totalMemoryKB: 0
|
||||||
|
property int usedMemoryKB: 0
|
||||||
|
property int totalSwapKB: 0
|
||||||
|
property int usedSwapKB: 0
|
||||||
|
|
||||||
|
// Network stats
|
||||||
|
property real networkRxRate: 0
|
||||||
|
property real networkTxRate: 0
|
||||||
|
property var lastNetworkStats: null
|
||||||
|
|
||||||
|
// Disk stats
|
||||||
|
property real diskReadRate: 0
|
||||||
|
property real diskWriteRate: 0
|
||||||
|
property var lastDiskStats: null
|
||||||
|
property var diskMounts: []
|
||||||
|
|
||||||
|
// History
|
||||||
|
property int historySize: 60
|
||||||
|
property var cpuHistory: []
|
||||||
|
property var memoryHistory: []
|
||||||
|
property var networkHistory: ({
|
||||||
|
"rx": [],
|
||||||
|
"tx": []
|
||||||
|
})
|
||||||
|
property var diskHistory: ({
|
||||||
|
"read": [],
|
||||||
|
"write": []
|
||||||
|
})
|
||||||
|
|
||||||
|
// System info
|
||||||
|
property string kernelVersion: ""
|
||||||
|
property string distribution: ""
|
||||||
|
property string hostname: ""
|
||||||
|
property string architecture: ""
|
||||||
|
property string loadAverage: ""
|
||||||
|
property int processCount: 0
|
||||||
|
property int threadCount: 0
|
||||||
|
property string bootTime: ""
|
||||||
|
property string motherboard: ""
|
||||||
|
property string biosVersion: ""
|
||||||
|
|
||||||
|
function addRef() {
|
||||||
|
refCount++;
|
||||||
|
if (refCount === 1) {
|
||||||
|
updateAllStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRef() {
|
||||||
|
refCount = Math.max(0, refCount - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAllStats() {
|
||||||
|
if (refCount > 0) {
|
||||||
|
isUpdating = true;
|
||||||
|
unifiedStatsProcess.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSortBy(newSortBy) {
|
||||||
|
if (newSortBy !== sortBy) {
|
||||||
|
sortBy = newSortBy;
|
||||||
|
sortProcessesInPlace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSortOrder() {
|
||||||
|
sortDescending = !sortDescending;
|
||||||
|
sortProcessesInPlace();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortProcessesInPlace() {
|
||||||
|
if (processes.length === 0) return;
|
||||||
|
|
||||||
|
const sortedProcesses = [...processes];
|
||||||
|
|
||||||
|
sortedProcesses.sort((a, b) => {
|
||||||
|
let aVal, bVal;
|
||||||
|
|
||||||
|
switch (sortBy) {
|
||||||
|
case "cpu":
|
||||||
|
aVal = parseFloat(a.cpu) || 0;
|
||||||
|
bVal = parseFloat(b.cpu) || 0;
|
||||||
|
break;
|
||||||
|
case "memory":
|
||||||
|
aVal = parseFloat(a.memoryPercent) || 0;
|
||||||
|
bVal = parseFloat(b.memoryPercent) || 0;
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
aVal = a.command || "";
|
||||||
|
bVal = b.command || "";
|
||||||
|
break;
|
||||||
|
case "pid":
|
||||||
|
aVal = parseInt(a.pid) || 0;
|
||||||
|
bVal = parseInt(b.pid) || 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
aVal = parseFloat(a.cpu) || 0;
|
||||||
|
bVal = parseFloat(b.cpu) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof aVal === "string") {
|
||||||
|
return sortDescending ? bVal.localeCompare(aVal) : aVal.localeCompare(bVal);
|
||||||
|
} else {
|
||||||
|
return sortDescending ? bVal - aVal : aVal - bVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
processes = sortedProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
function killProcess(pid) {
|
||||||
|
if (pid > 0) {
|
||||||
|
Quickshell.execDetached("kill", [pid.toString()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToHistory(array, value) {
|
||||||
|
array.push(value);
|
||||||
|
if (array.length > historySize)
|
||||||
|
array.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseUnifiedStats(text) {
|
||||||
|
function num(x) {
|
||||||
|
return (typeof x === "number" && !isNaN(x)) ? x : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(text);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("SysMonitorService: Failed to parse JSON:", error, "Raw text:", text.slice(0, 300));
|
||||||
|
isUpdating = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory
|
||||||
|
if (data.memory) {
|
||||||
|
const m = data.memory;
|
||||||
|
totalMemoryKB = num(m.total);
|
||||||
|
const free = num(m.free);
|
||||||
|
const buf = num(m.buffers);
|
||||||
|
const cached = num(m.cached);
|
||||||
|
usedMemoryKB = totalMemoryKB - free - buf - cached;
|
||||||
|
totalSwapKB = num(m.swaptotal);
|
||||||
|
usedSwapKB = num(m.swaptotal) - num(m.swapfree);
|
||||||
|
|
||||||
|
totalMemoryMB = totalMemoryKB / 1024;
|
||||||
|
usedMemoryMB = usedMemoryKB / 1024;
|
||||||
|
freeMemoryMB = (totalMemoryKB - usedMemoryKB) / 1024;
|
||||||
|
availableMemoryMB= (free + buf + cached) / 1024;
|
||||||
|
memoryUsage = totalMemoryKB > 0 ? (usedMemoryKB / totalMemoryKB) * 100 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU
|
||||||
|
if (data.cpu) {
|
||||||
|
cpuCores = data.cpu.count || 1;
|
||||||
|
cpuCount = data.cpu.count || 1;
|
||||||
|
cpuModel = data.cpu.model || "";
|
||||||
|
cpuFrequency = data.cpu.frequency || 0;
|
||||||
|
cpuTemperature = data.cpu.temperature || 0;
|
||||||
|
|
||||||
|
if (data.cpu.total && data.cpu.total.length >= 8) {
|
||||||
|
const user = data.cpu.total[0];
|
||||||
|
const nice = data.cpu.total[1];
|
||||||
|
const system = data.cpu.total[2];
|
||||||
|
const idle = data.cpu.total[3];
|
||||||
|
const iowait = data.cpu.total[4];
|
||||||
|
const irq = data.cpu.total[5];
|
||||||
|
const softirq = data.cpu.total[6];
|
||||||
|
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||||
|
const used = total - idle - iowait;
|
||||||
|
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||||
|
cpuUsage = usage;
|
||||||
|
totalCpuUsage = usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.cpu.cores) {
|
||||||
|
const coreUsages = [];
|
||||||
|
for (const coreStats of data.cpu.cores) {
|
||||||
|
if (coreStats && coreStats.length >= 8) {
|
||||||
|
const user = coreStats[0];
|
||||||
|
const nice = coreStats[1];
|
||||||
|
const system = coreStats[2];
|
||||||
|
const idle = coreStats[3];
|
||||||
|
const iowait = coreStats[4];
|
||||||
|
const irq = coreStats[5];
|
||||||
|
const softirq = coreStats[6];
|
||||||
|
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||||
|
const used = total - idle - iowait;
|
||||||
|
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||||
|
coreUsages.push(usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
perCoreCpuUsage = coreUsages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network
|
||||||
|
if (data.network) {
|
||||||
|
let totalRx = 0;
|
||||||
|
let totalTx = 0;
|
||||||
|
for (const iface of data.network) {
|
||||||
|
totalRx += iface.rx;
|
||||||
|
totalTx += iface.tx;
|
||||||
|
}
|
||||||
|
if (lastNetworkStats) {
|
||||||
|
const timeDiff = updateInterval / 1000;
|
||||||
|
const rxDiff = totalRx - lastNetworkStats.rx;
|
||||||
|
const txDiff = totalTx - lastNetworkStats.tx;
|
||||||
|
networkRxRate = Math.max(0, rxDiff / timeDiff);
|
||||||
|
networkTxRate = Math.max(0, txDiff / timeDiff);
|
||||||
|
addToHistory(networkHistory.rx, networkRxRate / 1024);
|
||||||
|
addToHistory(networkHistory.tx, networkTxRate / 1024);
|
||||||
|
}
|
||||||
|
lastNetworkStats = { "rx": totalRx, "tx": totalTx };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disk
|
||||||
|
if (data.disk) {
|
||||||
|
let totalRead = 0;
|
||||||
|
let totalWrite = 0;
|
||||||
|
for (const disk of data.disk) {
|
||||||
|
totalRead += disk.read * 512;
|
||||||
|
totalWrite += disk.write * 512;
|
||||||
|
}
|
||||||
|
if (lastDiskStats) {
|
||||||
|
const timeDiff = updateInterval / 1000;
|
||||||
|
const readDiff = totalRead - lastDiskStats.read;
|
||||||
|
const writeDiff = totalWrite - lastDiskStats.write;
|
||||||
|
diskReadRate = Math.max(0, readDiff / timeDiff);
|
||||||
|
diskWriteRate = Math.max(0, writeDiff / timeDiff);
|
||||||
|
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024));
|
||||||
|
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024));
|
||||||
|
}
|
||||||
|
lastDiskStats = { "read": totalRead, "write": totalWrite };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes
|
||||||
|
if (data.processes) {
|
||||||
|
const newProcesses = [];
|
||||||
|
for (const proc of data.processes) {
|
||||||
|
newProcesses.push({
|
||||||
|
"pid": proc.pid,
|
||||||
|
"ppid": proc.ppid,
|
||||||
|
"cpu": proc.cpu,
|
||||||
|
"memoryPercent": proc.memoryPercent,
|
||||||
|
"memoryKB": proc.memoryKB,
|
||||||
|
"command": proc.command,
|
||||||
|
"fullCommand": proc.fullCommand,
|
||||||
|
"displayName": proc.command.length > 15 ? proc.command.substring(0, 15) + "..." : proc.command
|
||||||
|
});
|
||||||
|
}
|
||||||
|
processes = newProcesses;
|
||||||
|
sortProcessesInPlace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// System info
|
||||||
|
if (data.system) {
|
||||||
|
kernelVersion = data.system.kernel || "";
|
||||||
|
distribution = data.system.distro || "";
|
||||||
|
hostname = data.system.hostname || "";
|
||||||
|
architecture = data.system.arch || "";
|
||||||
|
loadAverage = data.system.loadavg || "";
|
||||||
|
processCount = data.system.processes || 0;
|
||||||
|
threadCount = data.system.threads || 0;
|
||||||
|
bootTime = data.system.boottime || "";
|
||||||
|
motherboard = data.system.motherboard || "";
|
||||||
|
biosVersion = data.system.bios || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.diskmounts) {
|
||||||
|
diskMounts = data.diskmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update history
|
||||||
|
addToHistory(cpuHistory, cpuUsage);
|
||||||
|
addToHistory(memoryHistory, memoryUsage);
|
||||||
|
|
||||||
|
isUpdating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
function getProcessIcon(command) {
|
||||||
|
const cmd = command.toLowerCase();
|
||||||
|
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
|
||||||
|
return "web";
|
||||||
|
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
|
||||||
|
return "code";
|
||||||
|
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
|
||||||
|
return "terminal";
|
||||||
|
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
|
||||||
|
return "music_note";
|
||||||
|
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
|
||||||
|
return "play_circle";
|
||||||
|
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
|
||||||
|
return "settings";
|
||||||
|
return "memory";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCpuUsage(cpu) {
|
||||||
|
return (cpu || 0).toFixed(1) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMemoryUsage(memoryKB) {
|
||||||
|
const mem = memoryKB || 0;
|
||||||
|
if (mem < 1024)
|
||||||
|
return mem.toFixed(0) + " KB";
|
||||||
|
else if (mem < 1024 * 1024)
|
||||||
|
return (mem / 1024).toFixed(1) + " MB";
|
||||||
|
else
|
||||||
|
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSystemMemory(memoryKB) {
|
||||||
|
const mem = memoryKB || 0;
|
||||||
|
if (mem < 1024 * 1024)
|
||||||
|
return (mem / 1024).toFixed(0) + " MB";
|
||||||
|
else
|
||||||
|
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMemory(mb) {
|
||||||
|
const mem = mb || 0;
|
||||||
|
if (mem >= 1024)
|
||||||
|
return (mem / 1024).toFixed(1) + " GB";
|
||||||
|
return mem.toFixed(0) + " MB";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCpuUsageColor() {
|
||||||
|
if (cpuUsage > 80) return "#e74c3c";
|
||||||
|
if (cpuUsage > 60) return "#f39c12";
|
||||||
|
return "#27ae60";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMemoryUsageColor() {
|
||||||
|
if (memoryUsage > 90) return "#e74c3c";
|
||||||
|
if (memoryUsage > 75) return "#f39c12";
|
||||||
|
return "#3498db";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemperatureColor() {
|
||||||
|
if (cpuTemperature > 80) return "#e74c3c";
|
||||||
|
if (cpuTemperature > 65) return "#f39c12";
|
||||||
|
return "#27ae60";
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: updateTimer
|
||||||
|
interval: root.updateInterval
|
||||||
|
running: root.refCount > 0
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: root.updateAllStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string scriptBody: `set -Eeuo pipefail
|
||||||
|
trap 'echo "ERR at line $LINENO: $BASH_COMMAND (exit $?)" >&2' ERR
|
||||||
|
sort_key=\${1:-cpu}
|
||||||
|
max_procs=\${2:-20}
|
||||||
|
|
||||||
|
json_escape() { sed -e 's/\\\\/\\\\\\\\/g' -e 's/"/\\\\"/g' -e ':a;N;$!ba;s/\\n/\\\\n/g'; }
|
||||||
|
|
||||||
|
printf "{"
|
||||||
|
|
||||||
|
mem_line="$(awk '/^MemTotal:/{t=$2}
|
||||||
|
/^MemFree:/{f=$2}
|
||||||
|
/^Buffers:/{b=$2}
|
||||||
|
/^Cached:/{c=$2}
|
||||||
|
/^SwapTotal:/{st=$2}
|
||||||
|
/^SwapFree:/{sf=$2}
|
||||||
|
END{printf "%d %d %d %d %d %d",t,f,b,c,st,sf}' /proc/meminfo)"
|
||||||
|
read -r MT MF BU CA ST SF <<< "$mem_line"
|
||||||
|
|
||||||
|
printf '"memory":{"total":%d,"free":%d,"buffers":%d,"cached":%d,"swaptotal":%d,"swapfree":%d},' \\
|
||||||
|
"$MT" "$MF" "$BU" "$CA" "$ST" "$SF"
|
||||||
|
|
||||||
|
cpu_count=$(nproc)
|
||||||
|
cpu_model=$(grep -m1 'model name' /proc/cpuinfo | cut -d: -f2- | sed 's/^ *//' | json_escape || echo 'Unknown')
|
||||||
|
cpu_freq=$(awk -F: '/cpu MHz/{gsub(/ /,"",$2);print $2;exit}' /proc/cpuinfo || echo 0)
|
||||||
|
cpu_temp=$(if [ -r /sys/class/thermal/thermal_zone0/temp ]; then
|
||||||
|
awk '{printf "%.1f",$1/1000}' /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0
|
||||||
|
else echo 0; fi)
|
||||||
|
|
||||||
|
printf '"cpu":{"count":%d,"model":"%s","frequency":%s,"temperature":%s,' \\
|
||||||
|
"$cpu_count" "$cpu_model" "$cpu_freq" "$cpu_temp"
|
||||||
|
|
||||||
|
printf '"total":'
|
||||||
|
awk 'NR==1 {printf "[%d,%d,%d,%d,%d,%d,%d,%d]", $2,$3,$4,$5,$6,$7,$8,$9; exit}' /proc/stat
|
||||||
|
|
||||||
|
printf ',"cores":['
|
||||||
|
cpu_cores=$(nproc)
|
||||||
|
awk -v n="$cpu_cores" 'BEGIN{c=0}
|
||||||
|
/^cpu[0-9]+/ {
|
||||||
|
if(c>0) printf ",";
|
||||||
|
printf "[%d,%d,%d,%d,%d,%d,%d,%d]", $2,$3,$4,$5,$6,$7,$8,$9;
|
||||||
|
c++;
|
||||||
|
if(c==n) exit
|
||||||
|
}' /proc/stat
|
||||||
|
printf ']},'
|
||||||
|
|
||||||
|
printf '"network":['
|
||||||
|
tmp_net=$(mktemp)
|
||||||
|
grep -E '(wlan|eth|enp|wlp|ens|eno)' /proc/net/dev > "$tmp_net" || true
|
||||||
|
nfirst=1
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
iface=$(echo "$line" | awk '{print $1}' | sed 's/://')
|
||||||
|
rx_bytes=$(echo "$line" | awk '{print $2}')
|
||||||
|
tx_bytes=$(echo "$line" | awk '{print $10}')
|
||||||
|
[ $nfirst -eq 1 ] || printf ","
|
||||||
|
printf '{"name":"%s","rx":%d,"tx":%d}' "$iface" "$rx_bytes" "$tx_bytes"
|
||||||
|
nfirst=0
|
||||||
|
done < "$tmp_net"
|
||||||
|
rm -f "$tmp_net"
|
||||||
|
printf '],'
|
||||||
|
|
||||||
|
printf '"disk":['
|
||||||
|
tmp_disk=$(mktemp)
|
||||||
|
grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+|dm-[0-9]+|mmcblk[0-9]+) ' /proc/diskstats > "$tmp_disk" || true
|
||||||
|
dfirst=1
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
name=$(echo "$line" | awk '{print $3}')
|
||||||
|
read_sectors=$(echo "$line" | awk '{print $6}')
|
||||||
|
write_sectors=$(echo "$line" | awk '{print $10}')
|
||||||
|
[ $dfirst -eq 1 ] || printf ","
|
||||||
|
printf '{"name":"%s","read":%d,"write":%d}' "$name" "$read_sectors" "$write_sectors"
|
||||||
|
dfirst=0
|
||||||
|
done < "$tmp_disk"
|
||||||
|
rm -f "$tmp_disk"
|
||||||
|
printf ']',
|
||||||
|
|
||||||
|
printf '"processes":['
|
||||||
|
case "$sort_key" in
|
||||||
|
cpu) SORT_OPT="--sort=-pcpu" ;;
|
||||||
|
memory) SORT_OPT="--sort=-pmem" ;;
|
||||||
|
name) SORT_OPT="--sort=+comm" ;;
|
||||||
|
pid) SORT_OPT="--sort=+pid" ;;
|
||||||
|
*) SORT_OPT="--sort=-pcpu" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
tmp_ps=$(mktemp)
|
||||||
|
ps -eo pid,ppid,pcpu,pmem,rss,comm,cmd --no-headers $SORT_OPT | head -n "$max_procs" > "$tmp_ps" || true
|
||||||
|
|
||||||
|
pfirst=1
|
||||||
|
while IFS=' ' read -r pid ppid cpu memp memk comm rest; do
|
||||||
|
[ -z "$pid" ] && continue
|
||||||
|
cmd=$(printf "%s" "$rest" | json_escape)
|
||||||
|
[ $pfirst -eq 1 ] || printf ","
|
||||||
|
printf '{"pid":%s,"ppid":%s,"cpu":%s,"memoryPercent":%s,"memoryKB":%s,"command":"%s","fullCommand":"%s"}' \\
|
||||||
|
"$pid" "$ppid" "$cpu" "$memp" "$memk" "$comm" "$cmd"
|
||||||
|
pfirst=0
|
||||||
|
done < "$tmp_ps"
|
||||||
|
rm -f "$tmp_ps"
|
||||||
|
printf ']',
|
||||||
|
|
||||||
|
dmip="/sys/class/dmi/id"
|
||||||
|
[ -d "$dmip" ] || dmip="/sys/devices/virtual/dmi/id"
|
||||||
|
mb_vendor=$([ -r "$dmip/board_vendor" ] && cat "$dmip/board_vendor" | json_escape || echo "Unknown")
|
||||||
|
mb_name=$([ -r "$dmip/board_name" ] && cat "$dmip/board_name" | json_escape || echo "")
|
||||||
|
bios_ver=$([ -r "$dmip/bios_version" ] && cat "$dmip/bios_version" | json_escape || echo "Unknown")
|
||||||
|
bios_date=$([ -r "$dmip/bios_date" ] && cat "$dmip/bios_date" | json_escape || echo "")
|
||||||
|
|
||||||
|
kern_ver=$(uname -r | json_escape)
|
||||||
|
distro=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2- | tr -d '"' | json_escape || echo 'Unknown')
|
||||||
|
host_name=$(hostname | json_escape)
|
||||||
|
arch_name=$(uname -m)
|
||||||
|
load_avg=$(cut -d' ' -f1-3 /proc/loadavg)
|
||||||
|
proc_count=$(( $(ps aux | wc -l) - 1 ))
|
||||||
|
thread_count=$(ps -eL | wc -l)
|
||||||
|
boot_time=$(who -b 2>/dev/null | awk '{print $3, $4}' | json_escape || echo 'Unknown')
|
||||||
|
|
||||||
|
printf '"system":{"kernel":"%s","distro":"%s","hostname":"%s","arch":"%s","loadavg":"%s","processes":%d,"threads":%d,"boottime":"%s","motherboard":"%s %s","bios":"%s %s"},' \\
|
||||||
|
"$kern_ver" "$distro" "$host_name" "$arch_name" "$load_avg" "$proc_count" "$thread_count" "$boot_time" "$mb_vendor" "$mb_name" "$bios_ver" "$bios_date"
|
||||||
|
|
||||||
|
printf '"diskmounts":['
|
||||||
|
tmp_mounts=$(mktemp)
|
||||||
|
df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -vE '^(tmpfs|devtmpfs)' | head -n 10 > "$tmp_mounts" || true
|
||||||
|
mfirst=1
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
device=$(echo "$line" | awk '{print $1}' | json_escape)
|
||||||
|
mount=$(echo "$line" | awk '{print $2}' | json_escape)
|
||||||
|
fstype=$(echo "$line" | awk '{print $3}')
|
||||||
|
size=$(echo "$line" | awk '{print $4}')
|
||||||
|
used=$(echo "$line" | awk '{print $5}')
|
||||||
|
avail=$(echo "$line" | awk '{print $6}')
|
||||||
|
percent=$(echo "$line" | awk '{print $7}')
|
||||||
|
[ $mfirst -eq 1 ] || printf ","
|
||||||
|
printf '{"device":"%s","mount":"%s","fstype":"%s","size":"%s","used":"%s","avail":"%s","percent":"%s"}' \\
|
||||||
|
"$device" "$mount" "$fstype" "$size" "$used" "$avail" "$percent"
|
||||||
|
mfirst=0
|
||||||
|
done < "$tmp_mounts"
|
||||||
|
rm -f "$tmp_mounts"
|
||||||
|
printf ']'
|
||||||
|
|
||||||
|
printf "}\\n"`
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: unifiedStatsProcess
|
||||||
|
command: [
|
||||||
|
"bash", "-c",
|
||||||
|
"bash -s \"$1\" \"$2\" <<'QS_EOF'\\n" + root.scriptBody + "\\nQS_EOF\\n",
|
||||||
|
"qsmon", root.sortBy, root.maxProcesses
|
||||||
|
]
|
||||||
|
running: false
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
console.warn("Unified stats process failed with exit code:", exitCode);
|
||||||
|
isUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
if (text.trim()) {
|
||||||
|
const fullText = text.trim();
|
||||||
|
const lastBraceIndex = fullText.lastIndexOf('}');
|
||||||
|
|
||||||
|
if (lastBraceIndex === -1) {
|
||||||
|
console.error("SysMonitorService: No JSON object found in output.", fullText);
|
||||||
|
isUpdating = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonText = fullText.substring(0, lastBraceIndex + 1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(jsonText);
|
||||||
|
parseUnifiedStats(jsonText);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("BROKEN JSON:", e, "Cleaned Text:", jsonText);
|
||||||
|
isUpdating = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,551 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property real cpuUsage: 0
|
|
||||||
property int cpuCores: 1
|
|
||||||
property string cpuModel: ""
|
|
||||||
property real cpuFrequency: 0
|
|
||||||
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
property real memoryUsage: 0
|
|
||||||
property real totalMemory: 0
|
|
||||||
property real usedMemory: 0
|
|
||||||
property real freeMemory: 0
|
|
||||||
property real availableMemory: 0
|
|
||||||
property real bufferMemory: 0
|
|
||||||
property real cacheMemory: 0
|
|
||||||
property real cpuTemperature: 0
|
|
||||||
property string kernelVersion: ""
|
|
||||||
property string distribution: ""
|
|
||||||
property string hostname: ""
|
|
||||||
property string scheduler: ""
|
|
||||||
property string architecture: ""
|
|
||||||
property string loadAverage: ""
|
|
||||||
property int processCount: 0
|
|
||||||
property int threadCount: 0
|
|
||||||
property string bootTime: ""
|
|
||||||
property string motherboard: ""
|
|
||||||
property string biosVersion: ""
|
|
||||||
property var diskMounts: []
|
|
||||||
property string diskUsage: ""
|
|
||||||
property int cpuUpdateInterval: 3000
|
|
||||||
property int memoryUpdateInterval: 5000
|
|
||||||
property int temperatureUpdateInterval: 10000
|
|
||||||
property int systemInfoUpdateInterval: 30000
|
|
||||||
property bool enabledForTopBar: true
|
|
||||||
property bool enabledForDetailedView: false
|
|
||||||
|
|
||||||
function getCpuInfo() {
|
|
||||||
cpuInfoProcess.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSystemStats() {
|
|
||||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
|
||||||
cpuUsageProcess.running = true;
|
|
||||||
memoryUsageProcess.running = true;
|
|
||||||
cpuFrequencyProcess.running = true;
|
|
||||||
if (root.enabledForDetailedView)
|
|
||||||
temperatureProcess.running = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSystemInfo() {
|
|
||||||
kernelInfoProcess.running = true;
|
|
||||||
distributionProcess.running = true;
|
|
||||||
hostnameProcess.running = true;
|
|
||||||
schedulerProcess.running = true;
|
|
||||||
architectureProcess.running = true;
|
|
||||||
loadAverageProcess.running = true;
|
|
||||||
processCountProcess.running = true;
|
|
||||||
threadCountProcess.running = true;
|
|
||||||
bootTimeProcess.running = true;
|
|
||||||
motherboardProcess.running = true;
|
|
||||||
biosProcess.running = true;
|
|
||||||
diskMountsProcess.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableTopBarMonitoring(enabled) {
|
|
||||||
root.enabledForTopBar = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableDetailedMonitoring(enabled) {
|
|
||||||
root.enabledForDetailedView = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCpuUsageColor() {
|
|
||||||
if (cpuUsage > 80)
|
|
||||||
return "#e74c3c";
|
|
||||||
|
|
||||||
if (cpuUsage > 60)
|
|
||||||
return "#f39c12";
|
|
||||||
|
|
||||||
return "#27ae60";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMemoryUsageColor() {
|
|
||||||
if (memoryUsage > 90)
|
|
||||||
return "#e74c3c";
|
|
||||||
|
|
||||||
if (memoryUsage > 75)
|
|
||||||
return "#f39c12";
|
|
||||||
|
|
||||||
return "#3498db";
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatMemory(mb) {
|
|
||||||
if (mb >= 1024)
|
|
||||||
return (mb / 1024).toFixed(1) + " GB";
|
|
||||||
|
|
||||||
return mb.toFixed(0) + " MB";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTemperatureColor() {
|
|
||||||
if (cpuTemperature > 80)
|
|
||||||
return "#e74c3c";
|
|
||||||
|
|
||||||
if (cpuTemperature > 65)
|
|
||||||
return "#f39c12";
|
|
||||||
|
|
||||||
return "#27ae60";
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
console.log("SystemMonitorService: Starting initialization...");
|
|
||||||
getCpuInfo();
|
|
||||||
updateSystemStats();
|
|
||||||
updateSystemInfo();
|
|
||||||
console.log("SystemMonitorService: Initialization complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: cpuInfoProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("CPU info check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const lines = text.trim().split('\n');
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line.includes("Model name"))
|
|
||||||
root.cpuModel = line.split(":")[1].trim();
|
|
||||||
else if (line.includes("CPU(s):"))
|
|
||||||
root.cpuCores = parseInt(line.split(":")[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: cpuUsageProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("CPU usage check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim()) {
|
|
||||||
const stats = text.trim().split(" ").map((x) => {
|
|
||||||
return parseInt(x);
|
|
||||||
});
|
|
||||||
if (root.prevCpuStats[0] > 0) {
|
|
||||||
let diffs = [];
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
diffs[i] = stats[i] - root.prevCpuStats[i];
|
|
||||||
}
|
|
||||||
const totalTime = diffs.reduce((a, b) => {
|
|
||||||
return a + b;
|
|
||||||
}, 0);
|
|
||||||
const idleTime = diffs[3] + diffs[4];
|
|
||||||
if (totalTime > 0)
|
|
||||||
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100));
|
|
||||||
|
|
||||||
}
|
|
||||||
root.prevCpuStats = stats;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: memoryUsageProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Memory usage check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim()) {
|
|
||||||
const parts = text.trim().split(" ");
|
|
||||||
root.memoryUsage = parseFloat(parts[0]);
|
|
||||||
root.totalMemory = parseFloat(parts[1]);
|
|
||||||
root.usedMemory = parseFloat(parts[2]);
|
|
||||||
root.availableMemory = parseFloat(parts[3]);
|
|
||||||
root.freeMemory = root.totalMemory - root.usedMemory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: cpuFrequencyProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("CPU frequency check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.cpuFrequency = parseFloat(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: temperatureProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "if [ -f /sys/class/thermal/thermal_zone0/temp ]; then cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}'; else sensors 2>/dev/null | grep 'Core 0' | awk '{print $3}' | sed 's/+//g;s/°C//g' | head -1; fi"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("CPU temperature check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.cpuTemperature = parseFloat(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: kernelInfoProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "uname -r"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Kernel info check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.kernelVersion = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: distributionProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Distribution check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.distribution = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: hostnameProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "hostname"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Hostname check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.hostname = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: schedulerProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Scheduler check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.scheduler = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: architectureProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "uname -m"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Architecture check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.architecture = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: loadAverageProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Load average check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.loadAverage = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: processCountProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "ps aux | wc -l"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Process count check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.processCount = parseInt(text.trim()) - 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: threadCountProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Thread count check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.threadCount = parseInt(text.trim());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: bootTimeProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "who -b | awk '{print $3, $4}' || stat -c %w /proc/1 2>/dev/null | cut -d' ' -f1,2 || echo 'Unknown'"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Boot time check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.bootTime = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: motherboardProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/board_vendor ] && [ -r /sys/devices/virtual/dmi/id/board_name ]; then echo \"$(cat /sys/devices/virtual/dmi/id/board_vendor 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Motherboard check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.motherboard = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: biosProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/bios_version ] && [ -r /sys/devices/virtual/dmi/id/bios_date ]; then echo \"$(cat /sys/devices/virtual/dmi/id/bios_version 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/bios_date 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("BIOS check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim())
|
|
||||||
root.biosVersion = text.trim();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: diskMountsProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", "df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -v tmpfs | grep -v devtmpfs | head -10"]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0)
|
|
||||||
console.warn("Disk mounts check failed with exit code:", exitCode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (text.trim()) {
|
|
||||||
let mounts = [];
|
|
||||||
const lines = text.trim().split('\n');
|
|
||||||
for (const line of lines) {
|
|
||||||
const parts = line.split(/\s+/);
|
|
||||||
if (parts.length >= 7)
|
|
||||||
mounts.push({
|
|
||||||
"device": parts[0],
|
|
||||||
"mount": parts[1],
|
|
||||||
"fstype": parts[2],
|
|
||||||
"size": parts[3],
|
|
||||||
"used": parts[4],
|
|
||||||
"avail": parts[5],
|
|
||||||
"percent": parts[6]
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
root.diskMounts = mounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: basicStatsTimer
|
|
||||||
|
|
||||||
interval: 5000
|
|
||||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
cpuUsageProcess.running = true;
|
|
||||||
cpuFrequencyProcess.running = true;
|
|
||||||
memoryUsageProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: detailedStatsTimer
|
|
||||||
|
|
||||||
interval: 15000
|
|
||||||
running: root.enabledForDetailedView
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
temperatureProcess.running = true;
|
|
||||||
updateSystemInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -23,10 +23,6 @@ Singleton {
|
|||||||
uptimeProcess.running = true;
|
uptimeProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProfilePicture() {
|
|
||||||
profilePictureProcess.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshUserInfo() {
|
function refreshUserInfo() {
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
getUptime();
|
getUptime();
|
||||||
@@ -61,8 +57,6 @@ Singleton {
|
|||||||
root.fullName = parts[1] || parts[0] || "";
|
root.fullName = parts[1] || parts[0] || "";
|
||||||
root.hostname = parts[2] || "";
|
root.hostname = parts[2] || "";
|
||||||
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
|
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
|
||||||
// Try to find profile picture
|
|
||||||
getProfilePicture();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,52 +83,4 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for profile picture in common locations
|
|
||||||
Process {
|
|
||||||
id: profilePictureProcess
|
|
||||||
|
|
||||||
command: ["bash", "-c", `
|
|
||||||
# Try common profile picture locations
|
|
||||||
for path in \
|
|
||||||
"$HOME/.face" \
|
|
||||||
"$HOME/.face.icon" \
|
|
||||||
"/var/lib/AccountsService/icons/$USER" \
|
|
||||||
"/usr/share/pixmaps/faces/$USER" \
|
|
||||||
"/usr/share/pixmaps/faces/$USER.png" \
|
|
||||||
"/usr/share/pixmaps/faces/$USER.jpg"; do
|
|
||||||
if [ -f "$path" ]; then
|
|
||||||
echo "$path"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Fallback to generic user icon
|
|
||||||
echo ""
|
|
||||||
`]
|
|
||||||
running: false
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
console.warn("UserInfoService: Failed to find profile picture");
|
|
||||||
root.profilePicture = "";
|
|
||||||
root.profileAvailable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const path = text.trim();
|
|
||||||
if (path && path.length > 0) {
|
|
||||||
root.profilePicture = "file://" + path;
|
|
||||||
root.profileAvailable = true;
|
|
||||||
console.log("UserInfoService: Profile picture found at", path);
|
|
||||||
} else {
|
|
||||||
root.profilePicture = "";
|
|
||||||
root.profileAvailable = false;
|
|
||||||
console.log("UserInfoService: No profile picture found, using default avatar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
62
Widgets/CachingImage.qml
Normal file
62
Widgets/CachingImage.qml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string imagePath: ""
|
||||||
|
property string imageHash: ""
|
||||||
|
property int maxCacheSize: 512
|
||||||
|
|
||||||
|
readonly property string cachePath: imageHash ? `${Paths.stringify(Paths.imagecache)}/${imageHash}@${maxCacheSize}x${maxCacheSize}.png` : ""
|
||||||
|
|
||||||
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
sourceSize.width: maxCacheSize
|
||||||
|
sourceSize.height: maxCacheSize
|
||||||
|
smooth: true
|
||||||
|
|
||||||
|
onImagePathChanged: {
|
||||||
|
if (imagePath) {
|
||||||
|
hashProcess.command = ["sha256sum", Paths.strip(imagePath)]
|
||||||
|
hashProcess.running = true
|
||||||
|
} else {
|
||||||
|
source = ""
|
||||||
|
imageHash = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCachePathChanged: {
|
||||||
|
if (imageHash && cachePath) {
|
||||||
|
// Ensure cache directory exists before trying to load from cache
|
||||||
|
Paths.mkdir(Paths.imagecache)
|
||||||
|
source = cachePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (source == cachePath && status === Image.Error) {
|
||||||
|
source = imagePath
|
||||||
|
} else if (source == imagePath && status === Image.Ready && imageHash && cachePath) {
|
||||||
|
Paths.mkdir(Paths.imagecache)
|
||||||
|
const grabPath = cachePath
|
||||||
|
if (visible && width > 0 && height > 0 && Window.window && Window.window.visible) {
|
||||||
|
grabToImage(res => res.saveToFile(grabPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: hashProcess
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
root.imageHash = text.split(" ")[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtCore
|
import QtCore
|
||||||
@@ -30,6 +32,12 @@ DankModal {
|
|||||||
folder: currentPath ? "file://" + currentPath : "file://" + homeDir
|
folder: currentPath ? "file://" + currentPath : "file://" + homeDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isImageFile(fileName) {
|
||||||
|
if (!fileName) return false
|
||||||
|
var ext = fileName.toLowerCase().split('.').pop()
|
||||||
|
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(ext)
|
||||||
|
}
|
||||||
|
|
||||||
function getLastPath() {
|
function getLastPath() {
|
||||||
var lastPath = "";
|
var lastPath = "";
|
||||||
if (browserType === "wallpaper") {
|
if (browserType === "wallpaper") {
|
||||||
@@ -69,20 +77,13 @@ DankModal {
|
|||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
// Use last path or home directory when opening
|
|
||||||
var startPath = getLastPath();
|
var startPath = getLastPath();
|
||||||
console.log("Opening file browser, setting path to:", startPath);
|
|
||||||
currentPath = startPath;
|
currentPath = startPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentPathChanged: {
|
onCurrentPathChanged: {
|
||||||
console.log("Current path changed to:", currentPath);
|
// Path changed, model will update automatically
|
||||||
console.log("Model count:", folderModel.count);
|
|
||||||
// Log first few files to debug
|
|
||||||
for (var i = 0; i < Math.min(3, folderModel.count); i++) {
|
|
||||||
console.log("File", i, ":", folderModel.get(i, "fileName"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateUp() {
|
function navigateUp() {
|
||||||
@@ -90,20 +91,16 @@ DankModal {
|
|||||||
|
|
||||||
// Don't go above home directory
|
// Don't go above home directory
|
||||||
if (path === homeDir) {
|
if (path === homeDir) {
|
||||||
console.log("Already at home directory, can't go up");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastSlash = path.lastIndexOf('/');
|
var lastSlash = path.lastIndexOf('/');
|
||||||
if (lastSlash > 0) {
|
if (lastSlash > 0) {
|
||||||
var newPath = path.substring(0, lastSlash);
|
var newPath = path.substring(0, lastSlash);
|
||||||
// Make sure we don't go above home (check if newPath starts with homeDir)
|
|
||||||
if (newPath.startsWith(homeDir)) {
|
if (newPath.startsWith(homeDir)) {
|
||||||
console.log("Navigating up from", path, "to", newPath);
|
|
||||||
currentPath = newPath;
|
currentPath = newPath;
|
||||||
saveLastPath(newPath);
|
saveLastPath(newPath);
|
||||||
} else {
|
} else {
|
||||||
console.log("Would go above home directory, stopping at", homeDir);
|
|
||||||
currentPath = homeDir;
|
currentPath = homeDir;
|
||||||
saveLastPath(homeDir);
|
saveLastPath(homeDir);
|
||||||
}
|
}
|
||||||
@@ -207,10 +204,18 @@ DankModal {
|
|||||||
|
|
||||||
cellWidth: 150
|
cellWidth: 150
|
||||||
cellHeight: 130
|
cellHeight: 130
|
||||||
|
cacheBuffer: 260 // Only cache ~2 rows worth of items
|
||||||
|
|
||||||
model: folderModel
|
model: folderModel
|
||||||
|
|
||||||
delegate: StyledRect {
|
delegate: StyledRect {
|
||||||
|
id: delegateRoot
|
||||||
|
|
||||||
|
required property bool fileIsDir
|
||||||
|
required property string filePath
|
||||||
|
required property string fileName
|
||||||
|
required property url fileURL
|
||||||
|
|
||||||
width: 140
|
width: 140
|
||||||
height: 120
|
height: 120
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -228,26 +233,34 @@ DankModal {
|
|||||||
height: 60
|
height: 60
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
Image {
|
CachingImage {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: !model.fileIsDir ? model.fileURL : ""
|
imagePath: !delegateRoot.fileIsDir ? delegateRoot.filePath : ""
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
visible: !model.fileIsDir
|
visible: !delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)
|
||||||
asynchronous: true
|
maxCacheSize: 80
|
||||||
}
|
}
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
name: model.fileIsDir ? "folder" : "description"
|
name: "description"
|
||||||
size: Theme.iconSizeLarge
|
size: Theme.iconSizeLarge
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
visible: model.fileIsDir
|
visible: !delegateRoot.fileIsDir && !isImageFile(delegateRoot.fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "folder"
|
||||||
|
size: Theme.iconSizeLarge
|
||||||
|
color: Theme.primary
|
||||||
|
visible: delegateRoot.fileIsDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// File name
|
// File name
|
||||||
StyledText {
|
StyledText {
|
||||||
text: model.fileName || ""
|
text: delegateRoot.fileName || ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
width: 120
|
width: 120
|
||||||
@@ -266,10 +279,10 @@ DankModal {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.fileIsDir) {
|
if (delegateRoot.fileIsDir) {
|
||||||
navigateTo(model.filePath);
|
navigateTo(delegateRoot.filePath);
|
||||||
} else {
|
} else {
|
||||||
fileSelected(model.filePath);
|
fileSelected(delegateRoot.filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,14 +27,17 @@ Item {
|
|||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
visible: toggle.text
|
visible: toggle.text
|
||||||
|
disabled: !toggle.enabled
|
||||||
stateColor: Theme.primary
|
stateColor: Theme.primary
|
||||||
cornerRadius: parent.radius
|
cornerRadius: parent.radius
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (toggle.enabled) {
|
||||||
toggle.checked = !toggle.checked;
|
toggle.checked = !toggle.checked;
|
||||||
toggle.clicked();
|
toggle.clicked();
|
||||||
toggle.toggled(toggle.checked);
|
toggle.toggled(toggle.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ Item {
|
|||||||
text: toggle.text
|
text: toggle.text
|
||||||
font.pixelSize: Appearance.fontSize.normal
|
font.pixelSize: Appearance.fontSize.normal
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
opacity: toggle.enabled ? 1 : 0.4
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -81,8 +85,8 @@ Item {
|
|||||||
anchors.rightMargin: toggle.text ? Theme.spacingM : 0
|
anchors.rightMargin: toggle.text ? Theme.spacingM : 0
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: toggle.checked ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
color: (toggle.checked && toggle.enabled) ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
opacity: toggle.toggling ? 0.6 : 1
|
opacity: toggle.toggling ? 0.6 : (toggle.enabled ? 1 : 0.4)
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
id: toggleHandle
|
id: toggleHandle
|
||||||
@@ -92,7 +96,7 @@ Item {
|
|||||||
radius: 10
|
radius: 10
|
||||||
color: Theme.surface
|
color: Theme.surface
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
x: toggle.checked ? parent.width - width - 2 : 2
|
x: (toggle.checked && toggle.enabled) ? parent.width - width - 2 : 2
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -121,11 +125,13 @@ Item {
|
|||||||
stateColor: Theme.primary
|
stateColor: Theme.primary
|
||||||
cornerRadius: parent.radius
|
cornerRadius: parent.radius
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (toggle.enabled) {
|
||||||
toggle.checked = !toggle.checked;
|
toggle.checked = !toggle.checked;
|
||||||
toggle.clicked();
|
toggle.clicked();
|
||||||
toggle.toggled(toggle.checked);
|
toggle.toggled(toggle.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
shell.qml
11
shell.qml
@@ -11,6 +11,7 @@ import qs.Modules.Settings
|
|||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Modules.ControlCenter.Network
|
import qs.Modules.ControlCenter.Network
|
||||||
import qs.Modals
|
import qs.Modals
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
id: root
|
id: root
|
||||||
@@ -90,6 +91,10 @@ ShellRoot {
|
|||||||
id: spotlightModal
|
id: spotlightModal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClipboardHistoryModal {
|
||||||
|
id: clipboardHistoryModalPopup
|
||||||
|
}
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
id: processListModalLoader
|
id: processListModalLoader
|
||||||
active: false
|
active: false
|
||||||
@@ -125,8 +130,8 @@ ShellRoot {
|
|||||||
target: "processlist"
|
target: "processlist"
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast {
|
// Toast {
|
||||||
id: toastWidget
|
// id: toastWidget
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user