mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
meta cleanup and refactors
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -54,3 +54,6 @@ compile_commands.json
|
||||
*_qmlcache.qrc
|
||||
UNUSED
|
||||
.qmlls.ini
|
||||
|
||||
CLAUDE-activeContext.md
|
||||
CLAUDE-temp.md
|
||||
34
CLAUDE.md
34
CLAUDE.md
@@ -2,6 +2,40 @@
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## AI Guidance
|
||||
|
||||
* Ignore GEMINI.md and GEMINI-*.md files
|
||||
* After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
|
||||
* For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
||||
* Before you finish, please verify your solution
|
||||
* Do what has been asked; nothing more, nothing less.
|
||||
* NEVER create files unless they're absolutely necessary for achieving your goal.
|
||||
* ALWAYS prefer editing an existing file to creating a new one.
|
||||
* NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
|
||||
* When you update or modify core context files, also update markdown documentation and memory bank
|
||||
* When asked to commit changes, exclude CLAUDE.md and CLAUDE-*.md referenced memory bank system files from any commits.
|
||||
|
||||
## Memory Bank System
|
||||
|
||||
This project uses a structured memory bank system with specialized context files. Always check these files for relevant information before starting work:
|
||||
|
||||
### Core Context Files
|
||||
|
||||
* **CLAUDE-activeContext.md** - Current session state, goals, and progress (if exists)
|
||||
* **CLAUDE-patterns.md** - Established code patterns and conventions (if exists)
|
||||
* **CLAUDE-decisions.md** - Architecture decisions and rationale (if exists)
|
||||
* **CLAUDE-troubleshooting.md** - Common issues and proven solutions (if exists)
|
||||
* **CLAUDE-config-variables.md** - Configuration variables reference (if exists)
|
||||
* **CLAUDE-temp.md** - Temporary scratch pad (only read when referenced)
|
||||
|
||||
**Important:** Always reference the active context file first to understand what's currently being worked on and maintain session continuity.
|
||||
|
||||
### Memory Bank System Backups
|
||||
|
||||
When asked to backup Memory Bank System files, you will copy the core context files above and @.claude settings directory to directory @/path/to/backup-directory. If files already exist in the backup directory, you will overwrite them.
|
||||
|
||||
## Project Overview
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Quickshell-based desktop shell implementation with Material Design 3 dark theme. The shell provides a complete desktop environment experience with panels, widgets, and system integration services.
|
||||
|
||||
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.UPower
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -545,11 +546,11 @@ Singleton {
|
||||
|
||||
function getPowerProfileIcon(profile) {
|
||||
switch (profile) {
|
||||
case "power-saver":
|
||||
case PowerProfile.PowerSaver:
|
||||
return "battery_saver";
|
||||
case "balanced":
|
||||
case PowerProfile.Balanced:
|
||||
return "battery_std";
|
||||
case "performance":
|
||||
case PowerProfile.Performance:
|
||||
return "flash_on";
|
||||
default:
|
||||
return "settings";
|
||||
@@ -557,12 +558,13 @@ Singleton {
|
||||
}
|
||||
|
||||
function getPowerProfileLabel(profile) {
|
||||
console.log("Theme.getPowerProfileLabel called with profile:", profile);
|
||||
switch (profile) {
|
||||
case "power-saver":
|
||||
case PowerProfile.PowerSaver:
|
||||
return "Power Saver";
|
||||
case "balanced":
|
||||
case PowerProfile.Balanced:
|
||||
return "Balanced";
|
||||
case "performance":
|
||||
case PowerProfile.Performance:
|
||||
return "Performance";
|
||||
default:
|
||||
return profile.charAt(0).toUpperCase() + profile.slice(1);
|
||||
@@ -571,11 +573,11 @@ Singleton {
|
||||
|
||||
function getPowerProfileDescription(profile) {
|
||||
switch (profile) {
|
||||
case "power-saver":
|
||||
case PowerProfile.PowerSaver:
|
||||
return "Extend battery life";
|
||||
case "balanced":
|
||||
case PowerProfile.Balanced:
|
||||
return "Balance power and performance";
|
||||
case "performance":
|
||||
case PowerProfile.Performance:
|
||||
return "Prioritize performance";
|
||||
default:
|
||||
return "Custom power profile";
|
||||
|
||||
@@ -10,73 +10,44 @@ import "../Common/fuzzysort.js" as Fuzzy
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property list<DesktopEntry> applications: []
|
||||
property var applicationsByName: ({})
|
||||
property var applicationsByExec: ({})
|
||||
property bool ready: false
|
||||
property list<DesktopEntry> applications: Array.from(DesktopEntries.applications.values)
|
||||
.filter(app => !app.noDisplay)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
property var preppedApps: []
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
loadApplications()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
console.log("AppSearchService: DesktopEntries applicationsChanged signal received")
|
||||
console.log("AppSearchService: Current applications count before reload:", applications.length)
|
||||
loadApplications()
|
||||
property var applicationsByName: {
|
||||
var byName = {}
|
||||
for (var i = 0; i < applications.length; i++) {
|
||||
var app = applications[i]
|
||||
byName[app.name.toLowerCase()] = app
|
||||
}
|
||||
return byName
|
||||
}
|
||||
|
||||
|
||||
function loadApplications() {
|
||||
Qt.callLater(function() {
|
||||
var allApps = Array.from(DesktopEntries.applications.values)
|
||||
|
||||
applications = allApps
|
||||
.filter(app => !app.noDisplay)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
var byName = {}
|
||||
var byExec = {}
|
||||
|
||||
for (var i = 0; i < applications.length; i++) {
|
||||
var app = applications[i]
|
||||
byName[app.name.toLowerCase()] = app
|
||||
|
||||
var execProp = app.execString || ""
|
||||
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
|
||||
if (cleanExec) {
|
||||
byExec[cleanExec] = app
|
||||
}
|
||||
property var applicationsByExec: {
|
||||
var byExec = {}
|
||||
for (var i = 0; i < applications.length; i++) {
|
||||
var app = applications[i]
|
||||
var execProp = app.execString || ""
|
||||
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
|
||||
if (cleanExec) {
|
||||
byExec[cleanExec] = app
|
||||
}
|
||||
|
||||
applicationsByName = byName
|
||||
applicationsByExec = byExec
|
||||
|
||||
preppedApps = applications.map(app => ({
|
||||
name: Fuzzy.prepare(app.name || ""),
|
||||
comment: Fuzzy.prepare(app.comment || ""),
|
||||
entry: app
|
||||
}))
|
||||
|
||||
ready = true
|
||||
|
||||
console.log("AppSearchService: Loaded", applications.length, "applications")
|
||||
console.log("AppSearchService: Prepared", preppedApps.length, "apps for fuzzy search")
|
||||
console.log("AppSearchService: Ready status:", ready)
|
||||
})
|
||||
}
|
||||
return byExec
|
||||
}
|
||||
|
||||
property var preppedApps: applications.map(app => ({
|
||||
name: Fuzzy.prepare(app.name || ""),
|
||||
comment: Fuzzy.prepare(app.comment || ""),
|
||||
entry: app
|
||||
}))
|
||||
|
||||
function searchApplications(query) {
|
||||
if (!query || query.length === 0) {
|
||||
return applications
|
||||
}
|
||||
|
||||
if (!ready || preppedApps.length === 0) {
|
||||
if (preppedApps.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -167,18 +138,4 @@ Singleton {
|
||||
})
|
||||
}
|
||||
|
||||
function launchApp(app) {
|
||||
if (!app) {
|
||||
console.warn("AppSearchService: Cannot launch app, app is null")
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof app.execute === "function") {
|
||||
app.execute()
|
||||
return true
|
||||
}
|
||||
|
||||
console.warn("AppSearchService: Cannot launch app, no execute method")
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -8,108 +8,55 @@ import Quickshell.Io
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property list<var> ddcMonitors: []
|
||||
readonly property list<Monitor> monitors: variants.instances
|
||||
property bool brightnessAvailable: laptopBacklightAvailable || ddcMonitors.length > 0
|
||||
property bool brightnessAvailable: laptopBacklightAvailable || ddcAvailable
|
||||
property bool laptopBacklightAvailable: false
|
||||
property bool ddcAvailable: false
|
||||
property int brightnessLevel: 75
|
||||
property int laptopBrightnessLevel: 75
|
||||
|
||||
function getMonitorForScreen(screen: ShellScreen): var {
|
||||
return monitors.find(function(m) { return m.modelData === screen; });
|
||||
}
|
||||
|
||||
property var debounceTimer: Timer {
|
||||
id: debounceTimer
|
||||
interval: 50
|
||||
repeat: false
|
||||
property int pendingValue: 0
|
||||
onTriggered: {
|
||||
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
|
||||
if (focusedMonitor) {
|
||||
focusedMonitor.setBrightness(pendingValue / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var laptopDebounceTimer: Timer {
|
||||
id: laptopDebounceTimer
|
||||
interval: 50
|
||||
repeat: false
|
||||
property int pendingValue: 0
|
||||
onTriggered: {
|
||||
laptopBrightnessProcess.command = ["brightnessctl", "set", pendingValue + "%"];
|
||||
laptopBrightnessProcess.running = true;
|
||||
}
|
||||
}
|
||||
property int maxBrightness: 100
|
||||
property int currentRawBrightness: 0
|
||||
|
||||
function setBrightness(percentage) {
|
||||
if (root.laptopBacklightAvailable) {
|
||||
// Use laptop backlight control
|
||||
root.laptopBrightnessLevel = percentage;
|
||||
laptopDebounceTimer.pendingValue = percentage;
|
||||
laptopDebounceTimer.restart();
|
||||
} else {
|
||||
// Use external monitor control
|
||||
root.brightnessLevel = percentage;
|
||||
debounceTimer.pendingValue = percentage;
|
||||
debounceTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
function increaseBrightness(): void {
|
||||
if (root.laptopBacklightAvailable) {
|
||||
setBrightness(Math.min(100, root.laptopBrightnessLevel + 10));
|
||||
} else {
|
||||
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
|
||||
if (focusedMonitor)
|
||||
focusedMonitor.setBrightness(focusedMonitor.brightness + 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
function decreaseBrightness(): void {
|
||||
if (root.laptopBacklightAvailable) {
|
||||
setBrightness(Math.max(1, root.laptopBrightnessLevel - 10));
|
||||
} else {
|
||||
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
|
||||
if (focusedMonitor)
|
||||
focusedMonitor.setBrightness(focusedMonitor.brightness - 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
onMonitorsChanged: {
|
||||
ddcMonitors = [];
|
||||
if (root.brightnessAvailable) {
|
||||
ddcProc.running = true;
|
||||
}
|
||||
brightnessLevel = Math.max(1, Math.min(100, percentage));
|
||||
|
||||
// Update brightness level from laptop or first monitor
|
||||
if (root.laptopBacklightAvailable) {
|
||||
// Laptop brightness is already set by laptopBrightnessInitProcess
|
||||
} else if (monitors.length > 0) {
|
||||
root.brightnessLevel = Math.round(monitors[0].brightness * 100);
|
||||
if (laptopBacklightAvailable) {
|
||||
laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
|
||||
laptopBrightnessProcess.running = true;
|
||||
} else if (ddcAvailable) {
|
||||
console.log("Setting DDC brightness to:", brightnessLevel);
|
||||
Quickshell.execDetached(["ddcutil", "setvcp", "10", brightnessLevel.toString()]);
|
||||
}
|
||||
}
|
||||
|
||||
function increaseBrightness() {
|
||||
setBrightness(brightnessLevel + 10);
|
||||
}
|
||||
|
||||
function decreaseBrightness() {
|
||||
setBrightness(brightnessLevel - 10);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ddcAvailabilityChecker.running = true;
|
||||
laptopBacklightChecker.running = true;
|
||||
}
|
||||
|
||||
Variants {
|
||||
id: variants
|
||||
model: Quickshell.screens
|
||||
Monitor {}
|
||||
onLaptopBacklightAvailableChanged: {
|
||||
if (laptopBacklightAvailable) {
|
||||
laptopBrightnessInitProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
onDdcAvailableChanged: {
|
||||
if (ddcAvailable) {
|
||||
ddcBrightnessInitProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: ddcAvailabilityChecker
|
||||
command: ["which", "ddcutil"]
|
||||
onExited: function(exitCode) {
|
||||
root.brightnessAvailable = (exitCode === 0);
|
||||
if (root.brightnessAvailable) {
|
||||
ddcProc.running = true;
|
||||
}
|
||||
ddcAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +64,7 @@ Singleton {
|
||||
id: laptopBacklightChecker
|
||||
command: ["brightnessctl", "--list"]
|
||||
onExited: function(exitCode) {
|
||||
root.laptopBacklightAvailable = (exitCode === 0);
|
||||
if (root.laptopBacklightAvailable) {
|
||||
laptopBrightnessInitProcess.running = true;
|
||||
}
|
||||
laptopBacklightAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,10 +73,7 @@ Singleton {
|
||||
running: false
|
||||
|
||||
onExited: function(exitCode) {
|
||||
if (exitCode === 0) {
|
||||
// Update was successful
|
||||
root.brightnessLevel = root.laptopBrightnessLevel;
|
||||
} else {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Failed to set laptop brightness, exit code:", exitCode);
|
||||
}
|
||||
}
|
||||
@@ -146,9 +87,8 @@ Singleton {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
// Get max brightness to calculate percentage
|
||||
currentRawBrightness = parseInt(text.trim());
|
||||
laptopMaxBrightnessProcess.running = true;
|
||||
root.laptopBrightnessLevel = parseInt(text.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,10 +108,8 @@ Singleton {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const maxBrightness = parseInt(text.trim());
|
||||
const currentPercentage = Math.round((root.laptopBrightnessLevel / maxBrightness) * 100);
|
||||
root.laptopBrightnessLevel = currentPercentage;
|
||||
root.brightnessLevel = currentPercentage;
|
||||
maxBrightness = parseInt(text.trim());
|
||||
brightnessLevel = Math.round((currentRawBrightness / maxBrightness) * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,86 +122,28 @@ Singleton {
|
||||
}
|
||||
|
||||
Process {
|
||||
id: ddcProc
|
||||
command: ["ddcutil", "detect", "--brief"]
|
||||
id: ddcBrightnessInitProcess
|
||||
command: ["ddcutil", "getvcp", "10", "--brief"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.ddcMonitors = text.trim().split("\n\n").filter(function(d) { return d.startsWith("Display "); }).map(function(d) { return ({
|
||||
model: d.match(/Monitor:.*:(.*):.*/)?.[1] || "Unknown",
|
||||
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)?.[1] || "0"
|
||||
}); });
|
||||
} else {
|
||||
root.ddcMonitors = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
onExited: function(exitCode) {
|
||||
if (exitCode !== 0) {
|
||||
root.ddcMonitors = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Monitor: QtObject {
|
||||
id: monitor
|
||||
|
||||
required property ShellScreen modelData
|
||||
readonly property bool isDdc: root.ddcMonitors.some(function(m) { return m.model === modelData.model; })
|
||||
readonly property string busNum: root.ddcMonitors.find(function(m) { return m.model === modelData.model; })?.busNum ?? ""
|
||||
property real brightness: 0.75
|
||||
|
||||
readonly property Process initProc: Process {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const parts = text.trim().split(" ");
|
||||
if (parts.length >= 5) {
|
||||
const current = parseInt(parts[3]) || 75;
|
||||
const max = parseInt(parts[4]) || 100;
|
||||
monitor.brightness = current / max;
|
||||
root.brightnessLevel = Math.round(monitor.brightness * 100);
|
||||
}
|
||||
const parts = text.trim().split(" ");
|
||||
if (parts.length >= 5) {
|
||||
const current = parseInt(parts[3]) || 75;
|
||||
const max = parseInt(parts[4]) || 100;
|
||||
brightnessLevel = Math.round((current / max) * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
onExited: function(exitCode) {
|
||||
if (exitCode !== 0) {
|
||||
monitor.brightness = 0.75;
|
||||
root.brightnessLevel = 75;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setBrightness(value: real): void {
|
||||
value = Math.max(0, Math.min(1, value));
|
||||
const rounded = Math.round(value * 100);
|
||||
if (Math.round(brightness * 100) === rounded)
|
||||
return;
|
||||
|
||||
brightness = value;
|
||||
root.brightnessLevel = rounded;
|
||||
|
||||
if (isDdc && busNum) {
|
||||
Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded.toString()]);
|
||||
onExited: function(exitCode) {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Failed to get DDC brightness, exit code:", exitCode);
|
||||
brightnessLevel = 75;
|
||||
}
|
||||
}
|
||||
|
||||
onBusNumChanged: {
|
||||
if (isDdc && busNum) {
|
||||
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
|
||||
initProc.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(function() {
|
||||
if (isDdc && busNum) {
|
||||
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
|
||||
initProc.running = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,11 +39,9 @@ Singleton {
|
||||
|
||||
function loadEvents(startDate, endDate) {
|
||||
if (!root.khalAvailable) {
|
||||
console.warn("CalendarService: khal not available, skipping event loading");
|
||||
return ;
|
||||
}
|
||||
if (eventsProcess.running) {
|
||||
console.log("CalendarService: Event loading already in progress, skipping...");
|
||||
return ;
|
||||
}
|
||||
// Store last requested date range for refresh timer
|
||||
@@ -71,24 +69,9 @@ Singleton {
|
||||
|
||||
// Initialize on component completion
|
||||
Component.onCompleted: {
|
||||
console.log("CalendarService: Component completed, initializing...");
|
||||
checkKhalAvailability();
|
||||
}
|
||||
|
||||
// Periodic refresh timer
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
|
||||
interval: 60000
|
||||
running: root.khalAvailable
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (lastStartDate && lastEndDate)
|
||||
loadEvents(lastStartDate, lastEndDate);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Process for checking khal configuration
|
||||
Process {
|
||||
id: khalCheckProcess
|
||||
@@ -97,17 +80,10 @@ Singleton {
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
root.khalAvailable = (exitCode === 0);
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")");
|
||||
} else {
|
||||
console.log("CalendarService: khal configured and available");
|
||||
// Load current month events when khal becomes available
|
||||
if (exitCode === 0) {
|
||||
loadCurrentMonth();
|
||||
}
|
||||
}
|
||||
onStarted: {
|
||||
console.log("CalendarService: Checking khal configuration...");
|
||||
}
|
||||
}
|
||||
|
||||
// Process for loading events
|
||||
@@ -123,7 +99,6 @@ Singleton {
|
||||
root.isLoading = false;
|
||||
if (exitCode !== 0) {
|
||||
root.lastError = "Failed to load events (exit code: " + exitCode + ")";
|
||||
console.warn("CalendarService:", root.lastError);
|
||||
return ;
|
||||
}
|
||||
try {
|
||||
@@ -249,19 +224,14 @@ Singleton {
|
||||
}
|
||||
root.eventsByDate = newEventsByDate;
|
||||
root.lastError = "";
|
||||
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days");
|
||||
} catch (error) {
|
||||
root.lastError = "Failed to parse events JSON: " + error.toString();
|
||||
console.error("CalendarService:", root.lastError);
|
||||
root.eventsByDate = {
|
||||
};
|
||||
}
|
||||
// Reset for next run
|
||||
eventsProcess.rawOutput = "";
|
||||
}
|
||||
onStarted: {
|
||||
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"), "to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"));
|
||||
}
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
signal showAppLauncher()
|
||||
signal hideAppLauncher()
|
||||
signal toggleAppLauncher()
|
||||
signal showSpotlight()
|
||||
signal hideSpotlight()
|
||||
signal toggleSpotlight()
|
||||
signal showClipboardHistory()
|
||||
signal hideClipboardHistory()
|
||||
signal toggleClipboardHistory()
|
||||
}
|
||||
@@ -139,7 +139,6 @@ Singleton {
|
||||
if (preferenceComplete) {
|
||||
root.changingPreference = false
|
||||
root.targetPreference = ""
|
||||
preferenceTimeoutTimer.stop()
|
||||
console.log("Network preference change completed successfully")
|
||||
}
|
||||
}
|
||||
@@ -379,26 +378,8 @@ Singleton {
|
||||
}
|
||||
|
||||
function delayedRefreshNetworkStatus() {
|
||||
console.log("Delayed network status refresh...")
|
||||
refreshTimer.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 1000
|
||||
onTriggered: {
|
||||
refreshNetworkStatus()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: preferenceTimeoutTimer
|
||||
interval: 15000 // 15 seconds timeout
|
||||
onTriggered: {
|
||||
console.log("Network preference change timeout - resetting changingPreference state")
|
||||
root.changingPreference = false
|
||||
root.targetPreference = ""
|
||||
}
|
||||
console.log("Refreshing network status immediately...")
|
||||
refreshNetworkStatus()
|
||||
}
|
||||
|
||||
function setNetworkPreference(preference) {
|
||||
@@ -406,7 +387,6 @@ Singleton {
|
||||
root.userPreference = preference
|
||||
root.changingPreference = true
|
||||
root.targetPreference = preference
|
||||
preferenceTimeoutTimer.start()
|
||||
Prefs.setNetworkPreference(preference)
|
||||
|
||||
if (preference === "wifi") {
|
||||
|
||||
@@ -99,23 +99,13 @@ Singleton {
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0 && root.niriAvailable) {
|
||||
console.warn("NiriWorkspaceService: Event stream exited with code", exitCode, "restarting in 2 seconds")
|
||||
restartTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restart timer for event stream
|
||||
Timer {
|
||||
id: restartTimer
|
||||
interval: 2000
|
||||
onTriggered: {
|
||||
if (root.niriAvailable) {
|
||||
console.warn("NiriWorkspaceService: Event stream exited with code", exitCode, "restarting immediately")
|
||||
eventStreamProcess.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleNiriEvent(event) {
|
||||
if (event.WorkspacesChanged) {
|
||||
handleWorkspacesChanged(event.WorkspacesChanged)
|
||||
@@ -173,6 +163,9 @@ Singleton {
|
||||
currentOutput = activatedWs.output || ""
|
||||
|
||||
updateCurrentOutputWorkspaces()
|
||||
|
||||
// Force property change notifications
|
||||
allWorkspacesChanged()
|
||||
} else {
|
||||
focusedWorkspaceIndex = 0
|
||||
}
|
||||
|
||||
@@ -333,31 +333,6 @@ Singleton {
|
||||
console.log("ProcessMonitorService: Initialization complete");
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: testTimer
|
||||
|
||||
interval: 3000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Starting test monitoring...");
|
||||
enableMonitoring(true);
|
||||
stopTestTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stopTestTimer
|
||||
|
||||
interval: 8000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Stopping test monitoring...");
|
||||
enableMonitoring(false);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemInfoProcess
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ Singleton {
|
||||
property string kernelVersion: ""
|
||||
property string distribution: ""
|
||||
property string hostname: ""
|
||||
property string uptime: ""
|
||||
property string scheduler: ""
|
||||
property string architecture: ""
|
||||
property string loadAverage: ""
|
||||
@@ -61,7 +60,6 @@ Singleton {
|
||||
kernelInfoProcess.running = true;
|
||||
distributionProcess.running = true;
|
||||
hostnameProcess.running = true;
|
||||
uptimeProcess.running = true;
|
||||
schedulerProcess.running = true;
|
||||
architectureProcess.running = true;
|
||||
loadAverageProcess.running = true;
|
||||
@@ -319,26 +317,6 @@ Singleton {
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: uptimeProcess
|
||||
|
||||
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Uptime check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.uptime = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: schedulerProcess
|
||||
@@ -546,55 +524,27 @@ Singleton {
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: cpuTimer
|
||||
id: basicStatsTimer
|
||||
|
||||
interval: root.cpuUpdateInterval
|
||||
interval: 5000
|
||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
cpuUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
}
|
||||
cpuUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
memoryUsageProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: memoryTimer
|
||||
id: detailedStatsTimer
|
||||
|
||||
interval: root.memoryUpdateInterval
|
||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView)
|
||||
memoryUsageProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: temperatureTimer
|
||||
|
||||
interval: root.temperatureUpdateInterval
|
||||
interval: 15000
|
||||
running: root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabledForDetailedView)
|
||||
temperatureProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: systemInfoTimer
|
||||
|
||||
interval: root.systemInfoUpdateInterval
|
||||
running: root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabledForDetailedView)
|
||||
updateSystemInfo();
|
||||
|
||||
temperatureProcess.running = true;
|
||||
updateSystemInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Singleton {
|
||||
currentLevel = levelInfo;
|
||||
toastTimer.stop();
|
||||
if (toastQueue.length > 0)
|
||||
queueTimer.start();
|
||||
processQueue();
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ Singleton {
|
||||
currentMessage = toast.message;
|
||||
currentLevel = toast.level;
|
||||
toastVisible = true;
|
||||
toastTimer.interval = toast.level === levelError ? 8000 : toast.level === levelWarn ? 6000 : 5000;
|
||||
toastTimer.interval = toast.level === levelError ? 5000 : toast.level === levelWarn ? 4000 : 3000;
|
||||
toastTimer.start();
|
||||
}
|
||||
|
||||
@@ -73,13 +73,5 @@ Singleton {
|
||||
onTriggered: hideToast()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: queueTimer
|
||||
|
||||
interval: 500
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: processQueue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,17 +36,6 @@ Singleton {
|
||||
Component.onCompleted: {
|
||||
getUserInfo();
|
||||
getUptime();
|
||||
// Update uptime every minute
|
||||
uptimeTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: uptimeTimer
|
||||
|
||||
interval: 60000 // 1 minute
|
||||
running: false
|
||||
repeat: true
|
||||
onTriggered: getUptime()
|
||||
}
|
||||
|
||||
// Get username and full name
|
||||
@@ -96,7 +85,6 @@ Singleton {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.uptime = text.trim() || "Unknown";
|
||||
console.log("UserInfoService: Uptime updated -", root.uptime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,9 @@ Singleton {
|
||||
return ;
|
||||
|
||||
root.isScanning = true;
|
||||
console.log("Starting WiFi scan...");
|
||||
wifiScanner.running = true;
|
||||
savedWifiScanner.running = true;
|
||||
currentWifiInfo.running = true;
|
||||
// Fallback timer in case no networks are found
|
||||
fallbackTimer.start();
|
||||
}
|
||||
|
||||
@@ -133,7 +131,6 @@ Singleton {
|
||||
}
|
||||
|
||||
function updateCurrentWifiInfo() {
|
||||
console.log("Updating current WiFi info...");
|
||||
currentWifiInfo.running = true;
|
||||
}
|
||||
|
||||
@@ -254,7 +251,6 @@ Singleton {
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
root.isScanning = false;
|
||||
console.log("WiFi scan timeout - no networks found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,11 +270,7 @@ Singleton {
|
||||
interval: 20000
|
||||
running: root.autoRefreshEnabled
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.autoRefreshEnabled)
|
||||
root.scanWifi();
|
||||
|
||||
}
|
||||
onTriggered: root.scanWifi()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,11 +24,6 @@ PanelWindow {
|
||||
property int selectedIndex: 0
|
||||
|
||||
function updateFilteredModel() {
|
||||
if (!AppSearchService.ready) {
|
||||
filteredModel.clear();
|
||||
selectedIndex = 0;
|
||||
return ;
|
||||
}
|
||||
filteredModel.clear();
|
||||
selectedIndex = 0;
|
||||
var apps = [];
|
||||
@@ -118,7 +113,7 @@ PanelWindow {
|
||||
var selectedApp = filteredModel.get(selectedIndex);
|
||||
if (selectedApp.desktopEntry) {
|
||||
Prefs.addRecentApp(selectedApp.desktopEntry);
|
||||
AppSearchService.launchApp(selectedApp.desktopEntry);
|
||||
selectedApp.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(selectedApp.exec);
|
||||
}
|
||||
@@ -130,7 +125,7 @@ PanelWindow {
|
||||
// Try to find the desktop entry
|
||||
var app = AppSearchService.getAppByExec(exec);
|
||||
if (app) {
|
||||
AppSearchService.launchApp(app);
|
||||
app.execute();
|
||||
} else {
|
||||
// Fallback to direct execution
|
||||
var cleanExec = exec.replace(/%[fFuU]/g, "").trim();
|
||||
@@ -171,14 +166,12 @@ PanelWindow {
|
||||
visible: isVisible
|
||||
color: "transparent"
|
||||
Component.onCompleted: {
|
||||
if (AppSearchService.ready) {
|
||||
var allCategories = AppSearchService.getAllCategories();
|
||||
// Insert "Recents" after "All"
|
||||
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
updateFilteredModel();
|
||||
}
|
||||
var allCategories = AppSearchService.getAllCategories();
|
||||
// Insert "Recents" after "All"
|
||||
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
updateFilteredModel();
|
||||
recentApps = Prefs.getRecentApps(); // Load recent apps on startup
|
||||
}
|
||||
|
||||
@@ -194,7 +187,7 @@ PanelWindow {
|
||||
Timer {
|
||||
id: searchDebounceTimer
|
||||
|
||||
interval: 100
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: updateFilteredModel()
|
||||
}
|
||||
@@ -226,56 +219,21 @@ PanelWindow {
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onReadyChanged() {
|
||||
if (AppSearchService.ready) {
|
||||
var allCategories = AppSearchService.getAllCategories();
|
||||
// Insert "Recents" after "All"
|
||||
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
updateFilteredModel();
|
||||
}
|
||||
}
|
||||
|
||||
target: AppSearchService
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onApplicationsChanged() {
|
||||
console.log("AppLauncher: DesktopEntries.applicationsChanged signal received");
|
||||
// Update categories when applications change
|
||||
if (AppSearchService.ready) {
|
||||
console.log("AppLauncher: Updating categories and model due to applicationsChanged");
|
||||
var allCategories = AppSearchService.getAllCategories();
|
||||
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
updateFilteredModel();
|
||||
} else {
|
||||
console.log("AppLauncher: AppSearchService not ready, skipping update");
|
||||
}
|
||||
console.log("AppLauncher: Updating categories and model due to applicationsChanged");
|
||||
var allCategories = AppSearchService.getAllCategories();
|
||||
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
updateFilteredModel();
|
||||
}
|
||||
|
||||
target: DesktopEntries
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onShowAppLauncher() {
|
||||
launcher.show();
|
||||
}
|
||||
|
||||
function onHideAppLauncher() {
|
||||
launcher.hide();
|
||||
}
|
||||
|
||||
function onToggleAppLauncher() {
|
||||
launcher.toggle();
|
||||
}
|
||||
|
||||
target: LauncherService
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onRecentlyUsedAppsChanged() {
|
||||
recentApps = Prefs.getRecentApps();
|
||||
@@ -530,7 +488,7 @@ PanelWindow {
|
||||
var firstApp = filteredModel.get(0);
|
||||
if (firstApp.desktopEntry) {
|
||||
Prefs.addRecentApp(firstApp.desktopEntry);
|
||||
AppSearchService.launchApp(firstApp.desktopEntry);
|
||||
firstApp.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(firstApp.exec);
|
||||
}
|
||||
@@ -1033,7 +991,7 @@ PanelWindow {
|
||||
onClicked: {
|
||||
if (model.desktopEntry) {
|
||||
Prefs.addRecentApp(model.desktopEntry);
|
||||
AppSearchService.launchApp(model.desktopEntry);
|
||||
model.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(model.exec);
|
||||
}
|
||||
@@ -1107,7 +1065,7 @@ PanelWindow {
|
||||
onClicked: {
|
||||
if (model.desktopEntry) {
|
||||
Prefs.addRecentApp(model.desktopEntry);
|
||||
AppSearchService.launchApp(model.desktopEntry);
|
||||
model.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(model.exec);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,12 @@ PanelWindow {
|
||||
|
||||
function setProfile(profile) {
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
errorToast.show();
|
||||
ToastService.showError("power-profiles-daemon not available");
|
||||
return ;
|
||||
}
|
||||
PowerProfiles.profile = profile;
|
||||
if (PowerProfiles.profile !== profile)
|
||||
errorToast.show();
|
||||
else
|
||||
console.log("Set power profile to: " + PowerProfile.toString(profile));
|
||||
ToastService.showError("Failed to set power profile");
|
||||
}
|
||||
|
||||
visible: batteryPopupVisible
|
||||
@@ -367,7 +365,7 @@ PanelWindow {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: Theme.getPowerProfileIcon(PowerProfile.toString(modelData))
|
||||
text: Theme.getPowerProfileIcon(modelData)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
|
||||
@@ -379,14 +377,14 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: Theme.getPowerProfileLabel(PowerProfile.toString(modelData))
|
||||
text: Theme.getPowerProfileLabel(modelData)
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
|
||||
font.weight: batteryControlPopup.isActiveProfile(modelData) ? Font.Medium : Font.Normal
|
||||
}
|
||||
|
||||
Text {
|
||||
text: Theme.getPowerProfileDescription(PowerProfile.toString(modelData))
|
||||
text: Theme.getPowerProfileDescription(modelData)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
}
|
||||
@@ -483,40 +481,5 @@ PanelWindow {
|
||||
|
||||
}
|
||||
|
||||
// Error toast
|
||||
Rectangle {
|
||||
id: errorToast
|
||||
|
||||
function show() {
|
||||
visible = true;
|
||||
hideTimer.restart();
|
||||
}
|
||||
|
||||
width: Math.min(300, parent.width - Theme.spacingL * 2)
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Theme.spacingL
|
||||
visible: false
|
||||
z: 1000
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "power-profiles-daemon not available"
|
||||
color: "white"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
|
||||
interval: 3000
|
||||
onTriggered: errorToast.visible = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,10 +31,6 @@ Column {
|
||||
loadEventsForMonth();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
console.log("CalendarWidget: Component completed, CalendarService available:", !!CalendarService);
|
||||
if (CalendarService)
|
||||
console.log("CalendarWidget: khal available:", CalendarService.khalAvailable);
|
||||
|
||||
loadEventsForMonth();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ PanelWindow {
|
||||
property bool calendarVisible: false
|
||||
|
||||
visible: calendarVisible
|
||||
onVisibleChanged: {
|
||||
if (visible && CalendarService) {
|
||||
CalendarService.loadCurrentMonth();
|
||||
}
|
||||
}
|
||||
implicitWidth: 480
|
||||
implicitHeight: 600
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
|
||||
@@ -23,9 +23,9 @@ Rectangle {
|
||||
|
||||
onActivePlayerChanged: {
|
||||
if (!activePlayer)
|
||||
clearCacheTimer.restart();
|
||||
updateTimer.start();
|
||||
else
|
||||
clearCacheTimer.stop();
|
||||
updateTimer.stop();
|
||||
}
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
@@ -36,30 +36,28 @@ Rectangle {
|
||||
layer.enabled: true
|
||||
|
||||
Timer {
|
||||
id: clearCacheTimer
|
||||
id: updateTimer
|
||||
|
||||
interval: 2000
|
||||
running: {
|
||||
// Run when no active player (for cache clearing) OR when playing (for position updates)
|
||||
return (!activePlayer) ||
|
||||
(activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing &&
|
||||
activePlayer.length > 0 && !progressMouseArea.isSeeking);
|
||||
}
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (!activePlayer) {
|
||||
// Clear cache when no player
|
||||
lastValidTitle = "";
|
||||
lastValidArtist = "";
|
||||
lastValidAlbum = "";
|
||||
lastValidArtUrl = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates progress bar every 2 seconds when playing
|
||||
Timer {
|
||||
id: positionTimer
|
||||
|
||||
interval: 2000
|
||||
running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking)
|
||||
stop(); // Stop after clearing cache
|
||||
} else if (activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
|
||||
// Update position when playing
|
||||
currentPosition = activePlayer.position;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
@@ -23,6 +24,10 @@ PanelWindow {
|
||||
if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering) {
|
||||
BluetoothService.adapter.discovering = false;
|
||||
}
|
||||
// Refresh uptime when opened
|
||||
if (visible && UserInfoService) {
|
||||
UserInfoService.getUptime();
|
||||
}
|
||||
}
|
||||
implicitWidth: 600
|
||||
implicitHeight: 500
|
||||
@@ -120,7 +125,7 @@ PanelWindow {
|
||||
}
|
||||
]
|
||||
|
||||
Column {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
@@ -698,7 +703,7 @@ PanelWindow {
|
||||
// Tab content area
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: root.powerOptionsExpanded ? 240 : 300
|
||||
Layout.fillHeight: true
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)
|
||||
|
||||
|
||||
@@ -11,6 +11,16 @@ ScrollView {
|
||||
id: displayTab
|
||||
|
||||
clip: true
|
||||
|
||||
property var brightnessDebounceTimer: Timer {
|
||||
interval: BrightnessService.ddcAvailable ? 500 : 50 // 500ms for slow DDC (i2c), 50ms for fast laptop backlight
|
||||
repeat: false
|
||||
property int pendingValue: 0
|
||||
onTriggered: {
|
||||
console.log("Debounce timer fired, setting brightness to:", pendingValue);
|
||||
BrightnessService.setBrightness(pendingValue);
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
@@ -36,8 +46,23 @@ ScrollView {
|
||||
rightIcon: "brightness_high"
|
||||
enabled: BrightnessService.brightnessAvailable
|
||||
onSliderValueChanged: function(newValue) {
|
||||
BrightnessService.setBrightness(newValue);
|
||||
console.log("Slider changed to:", newValue);
|
||||
brightnessDebounceTimer.pendingValue = newValue;
|
||||
brightnessDebounceTimer.restart();
|
||||
}
|
||||
onSliderDragFinished: function(finalValue) {
|
||||
console.log("Drag finished, immediate set:", finalValue);
|
||||
brightnessDebounceTimer.stop();
|
||||
BrightnessService.setBrightness(finalValue);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "using ddc - changes may take a moment to apply"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
visible: BrightnessService.ddcAvailable && !BrightnessService.laptopBacklightAvailable
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
z: 10
|
||||
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
|
||||
visible: networkTab.networkStatus !== "ethernet"
|
||||
visible: NetworkService.networkStatus !== "ethernet" && NetworkService.wifiAvailable && NetworkService.wifiEnabled
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
@@ -330,197 +330,215 @@ Item {
|
||||
id: wifiContent
|
||||
|
||||
width: parent.width
|
||||
spacing: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// WiFi toggle control (only show if WiFi hardware is available)
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: wifiToggleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
visible: NetworkService.wifiAvailable
|
||||
opacity: NetworkService.wifiToggling ? 0.6 : 1
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
id: wifiToggleIcon
|
||||
|
||||
text: NetworkService.wifiToggling ? "sync" : "power_settings_new"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rotation: NetworkService.wifiToggling ? wifiToggleIcon.rotation : 0
|
||||
|
||||
RotationAnimation {
|
||||
target: wifiToggleIcon
|
||||
property: "rotation"
|
||||
running: NetworkService.wifiToggling
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
|
||||
Behavior on rotation {
|
||||
RotationAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetworkService.wifiToggling ? "Switching WiFi..." : (NetworkService.wifiEnabled ? "Turn WiFi Off" : "Turn WiFi On")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiToggleArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
NetworkService.toggleWifiRadio();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Current WiFi connection (if connected)
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 80
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
||||
border.color: networkTab.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: networkTab.networkStatus === "wifi" ? 2 : 1
|
||||
visible: NetworkService.wifiAvailable && NetworkService.wifiEnabled
|
||||
border.color: NetworkService.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: NetworkService.networkStatus === "wifi" ? 2 : 1
|
||||
visible: NetworkService.wifiAvailable
|
||||
|
||||
Row {
|
||||
// WiFi icon
|
||||
Text {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
text: {
|
||||
if (!NetworkService.wifiEnabled) {
|
||||
return "wifi_off";
|
||||
} else if (NetworkService.networkStatus === "wifi") {
|
||||
return WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi";
|
||||
} else {
|
||||
return "wifi";
|
||||
}
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
// WiFi info text
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL + Theme.iconSize + Theme.spacingM
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: networkTab.networkStatus === "wifi" ? (WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi") : "wifi"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
if (!NetworkService.wifiEnabled) {
|
||||
return "WiFi is off";
|
||||
} else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
|
||||
return WifiService.currentWifiSSID || "Connected";
|
||||
} else {
|
||||
return "Not Connected";
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 4
|
||||
Text {
|
||||
text: {
|
||||
if (!NetworkService.wifiEnabled) {
|
||||
return "Turn on WiFi to see available networks";
|
||||
} else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
|
||||
return NetworkService.wifiIP || "Connected";
|
||||
} else {
|
||||
return "Select a network below";
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
}
|
||||
}
|
||||
|
||||
// WiFi toggle switch
|
||||
Rectangle {
|
||||
width: 48
|
||||
height: 24
|
||||
radius: 12
|
||||
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: NetworkService.wifiToggling ? 0.6 : 1
|
||||
|
||||
Rectangle {
|
||||
id: toggleHandle
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: Theme.surface
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: NetworkService.wifiEnabled ? parent.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
// Subtle shadow/glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 2
|
||||
height: parent.height + 2
|
||||
radius: (parent.width + 2) / 2
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(0, 0, 0, 0.1)
|
||||
border.width: 1
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiToggleArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
NetworkService.toggleWifiRadio();
|
||||
// Refresh network status and WiFi info after toggle with delay
|
||||
refreshTimer.triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force WiFi preference button
|
||||
Rectangle {
|
||||
width: 150
|
||||
height: 30
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface
|
||||
border.color: Theme.primary
|
||||
border.width: 1
|
||||
radius: 6
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
|
||||
visible: NetworkService.networkStatus !== "wifi" && NetworkService.ethernetConnected && NetworkService.wifiEnabled
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: NetworkService.networkStatus === "wifi" ? (WifiService.currentWifiSSID || "Connected") : "Not Connected"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||
id: wifiPreferenceIcon
|
||||
|
||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||
visible: networkTab.changingNetworkPreference
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rotation: networkTab.changingNetworkPreference ? wifiPreferenceIcon.rotation : 0
|
||||
|
||||
RotationAnimation {
|
||||
target: wifiPreferenceIcon
|
||||
property: "rotation"
|
||||
running: networkTab.changingNetworkPreference
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over Ethernet"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetworkService.networkStatus === "wifi" ? (NetworkService.wifiIP || "Connected") : "Select a network below"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Force WiFi preference button
|
||||
Rectangle {
|
||||
width: 150
|
||||
height: 30
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface
|
||||
border.color: Theme.primary
|
||||
border.width: 1
|
||||
radius: 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
|
||||
visible: networkTab.networkStatus !== "wifi"
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
id: wifiPreferenceIcon
|
||||
|
||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||
visible: networkTab.changingNetworkPreference
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rotation: networkTab.changingNetworkPreference ? wifiPreferenceIcon.rotation : 0
|
||||
|
||||
RotationAnimation {
|
||||
target: wifiPreferenceIcon
|
||||
property: "rotation"
|
||||
running: networkTab.changingNetworkPreference
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetworkService.changingNetworkPreference ? "Switching..." : (NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
propagateComposedEvents: false
|
||||
enabled: !networkTab.changingNetworkPreference
|
||||
onClicked: {
|
||||
console.log("Force WiFi preference clicked");
|
||||
if (NetworkService.networkStatus !== "wifi")
|
||||
NetworkService.setNetworkPreference("wifi");
|
||||
else
|
||||
NetworkService.setNetworkPreference("auto");
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
propagateComposedEvents: false
|
||||
enabled: !networkTab.changingNetworkPreference
|
||||
onClicked: {
|
||||
console.log("Force WiFi preference clicked");
|
||||
if (NetworkService.networkStatus !== "wifi")
|
||||
NetworkService.setNetworkPreference("wifi");
|
||||
else
|
||||
NetworkService.setNetworkPreference("auto");
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -532,7 +550,7 @@ Item {
|
||||
// Available WiFi Networks
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
visible: NetworkService.wifiEnabled
|
||||
|
||||
Row {
|
||||
@@ -607,7 +625,7 @@ Item {
|
||||
// Connection status indicator
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 40
|
||||
height: 32
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (WifiService.connectionStatus === "connecting")
|
||||
@@ -733,7 +751,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
height: 42
|
||||
radius: Theme.cornerRadiusSmall
|
||||
color: networkArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
border.color: modelData.connected ? Theme.primary : "transparent"
|
||||
@@ -741,7 +759,7 @@ Item {
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.margins: Theme.spacingS
|
||||
|
||||
// Signal strength icon
|
||||
Text {
|
||||
@@ -758,9 +776,9 @@ Item {
|
||||
// Network info
|
||||
Column {
|
||||
anchors.left: signalIcon.right
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: rightIcons.left
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
@@ -916,4 +934,30 @@ Item {
|
||||
|
||||
}
|
||||
|
||||
// Timer for refreshing network status after WiFi toggle
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 2000
|
||||
running: networkTab.visible && refreshTimer.triggered
|
||||
property bool triggered: false
|
||||
onTriggered: {
|
||||
NetworkService.refreshNetworkStatus();
|
||||
if (NetworkService.wifiEnabled) {
|
||||
WifiService.scanWifi();
|
||||
}
|
||||
triggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-refresh when WiFi state changes
|
||||
Connections {
|
||||
target: NetworkService
|
||||
function onWifiEnabledChanged() {
|
||||
if (NetworkService.wifiEnabled && networkTab.visible) {
|
||||
// When WiFi is enabled, scan and update info (only if tab is visible)
|
||||
WifiService.scanWifi();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ Item {
|
||||
signal sliderDragFinished(int finalValue)
|
||||
|
||||
height: 80
|
||||
|
||||
property bool isDragging: false
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
@@ -139,7 +141,8 @@ Item {
|
||||
preventStealing: true
|
||||
onPressed: (mouse) => {
|
||||
if (slider.enabled) {
|
||||
isDragging = true;
|
||||
slider.isDragging = true;
|
||||
sliderMouseArea.isDragging = true;
|
||||
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||
slider.value = newValue;
|
||||
@@ -148,12 +151,13 @@ Item {
|
||||
}
|
||||
onReleased: {
|
||||
if (slider.enabled) {
|
||||
isDragging = false;
|
||||
slider.isDragging = false;
|
||||
sliderMouseArea.isDragging = false;
|
||||
slider.sliderDragFinished(slider.value);
|
||||
}
|
||||
}
|
||||
onPositionChanged: (mouse) => {
|
||||
if (pressed && isDragging && slider.enabled) {
|
||||
if (pressed && slider.isDragging && slider.enabled) {
|
||||
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||
slider.value = newValue;
|
||||
@@ -161,7 +165,7 @@ Item {
|
||||
}
|
||||
}
|
||||
onClicked: (mouse) => {
|
||||
if (slider.enabled) {
|
||||
if (slider.enabled && !slider.isDragging) {
|
||||
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||
slider.value = newValue;
|
||||
@@ -175,11 +179,11 @@ Item {
|
||||
id: sliderGlobalMouseArea
|
||||
|
||||
anchors.fill: sliderContainer
|
||||
enabled: sliderMouseArea.isDragging
|
||||
enabled: slider.isDragging
|
||||
visible: false
|
||||
preventStealing: true
|
||||
onPositionChanged: (mouse) => {
|
||||
if (sliderMouseArea.isDragging && slider.enabled) {
|
||||
if (slider.isDragging && slider.enabled) {
|
||||
let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y);
|
||||
let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width));
|
||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||
@@ -188,7 +192,8 @@ Item {
|
||||
}
|
||||
}
|
||||
onReleased: {
|
||||
if (sliderMouseArea.isDragging && slider.enabled) {
|
||||
if (slider.isDragging && slider.enabled) {
|
||||
slider.isDragging = false;
|
||||
sliderMouseArea.isDragging = false;
|
||||
slider.sliderDragFinished(slider.value);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ PanelWindow {
|
||||
ProcessMonitorService.updateProcessList();
|
||||
SystemMonitorService.enableDetailedMonitoring(true);
|
||||
SystemMonitorService.updateSystemInfo();
|
||||
UserInfoService.getUptime();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
@@ -1479,7 +1480,7 @@ PanelWindow {
|
||||
}
|
||||
|
||||
Text {
|
||||
text: SystemMonitorService.uptime
|
||||
text: UserInfoService.uptime
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
|
||||
@@ -58,12 +58,6 @@ PanelWindow {
|
||||
}
|
||||
|
||||
function updateFilteredApps() {
|
||||
if (!AppSearchService.ready) {
|
||||
filteredApps = [];
|
||||
selectedIndex = 0;
|
||||
filteredModel.clear();
|
||||
return ;
|
||||
}
|
||||
filteredApps = [];
|
||||
selectedIndex = 0;
|
||||
var apps = [];
|
||||
@@ -149,7 +143,7 @@ PanelWindow {
|
||||
function launchApp(app) {
|
||||
Prefs.addRecentApp(app);
|
||||
if (app.desktopEntry) {
|
||||
AppSearchService.launchApp(app.desktopEntry);
|
||||
app.desktopEntry.execute();
|
||||
} else {
|
||||
var cleanExec = app.exec.replace(/%[fFuU]/g, "").trim();
|
||||
console.log("Spotlight: Launching app directly:", cleanExec);
|
||||
@@ -213,23 +207,21 @@ PanelWindow {
|
||||
color: "transparent"
|
||||
Component.onCompleted: {
|
||||
console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!");
|
||||
if (AppSearchService.ready) {
|
||||
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
|
||||
return cat !== "Education" && cat !== "Science";
|
||||
});
|
||||
// Insert "Recents" after "All"
|
||||
var result = ["All", "Recents"];
|
||||
categories = result.concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
}
|
||||
var allCategories = AppSearchService.getAllCategories().filter((cat) => {
|
||||
return cat !== "Education" && cat !== "Science";
|
||||
});
|
||||
// Insert "Recents" after "All"
|
||||
var result = ["All", "Recents"];
|
||||
categories = result.concat(allCategories.filter((cat) => {
|
||||
return cat !== "All";
|
||||
}));
|
||||
}
|
||||
|
||||
// Search debouncing
|
||||
Timer {
|
||||
id: searchDebounceTimer
|
||||
|
||||
interval: 100
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: updateFilteredApps()
|
||||
}
|
||||
@@ -262,7 +254,7 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
target: AppSearchService
|
||||
target: DesktopEntries
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -5,13 +5,17 @@ import qs.Services
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
signal clicked()
|
||||
|
||||
property bool isActive: false
|
||||
|
||||
readonly property bool nerdFontAvailable: Qt.fontFamilies()
|
||||
.indexOf("Symbols Nerd Font") !== -1
|
||||
|
||||
width: 40
|
||||
height: 30
|
||||
radius: Theme.cornerRadius
|
||||
color: launcherArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||
color: launcherArea.containsMouse || isActive ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
@@ -28,9 +32,7 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
LauncherService.toggleAppLauncher();
|
||||
}
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
|
||||
@@ -67,7 +67,7 @@ Rectangle {
|
||||
trayItem.activate();
|
||||
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
if (trayItem.hasMenu)
|
||||
if (trayItem && trayItem.hasMenu)
|
||||
customTrayMenu.showMenu(mouse.x, mouse.y);
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +126,10 @@ PanelWindow {
|
||||
|
||||
LauncherButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
isActive: launcher.isVisible
|
||||
onClicked: {
|
||||
appLauncher.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
WorkspaceSwitcher {
|
||||
|
||||
@@ -95,7 +95,7 @@ Rectangle {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", sequentialNumber.toString()]);
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", modelData.toString()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ PanelWindow {
|
||||
QsMenuOpener {
|
||||
id: menuOpener
|
||||
|
||||
menu: currentTrayItem ? currentTrayItem.menu : null
|
||||
menu: currentTrayItem && currentTrayItem.hasMenu ? currentTrayItem.menu : null
|
||||
}
|
||||
|
||||
// Custom menu styling using ListView
|
||||
@@ -93,12 +93,7 @@ PanelWindow {
|
||||
text: "M"
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: menuOpener.children ? [pe_unknown].filter((item) => {
|
||||
// Filter out empty items and separators
|
||||
return item && item.text && item.text.trim().length > 0 && !item.isSeparator;
|
||||
}) : []
|
||||
}
|
||||
model: menuOpener.children
|
||||
|
||||
delegate: Rectangle {
|
||||
width: ListView.view.width
|
||||
|
||||
Reference in New Issue
Block a user