mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 14:05:38 -05:00
Many bug fixes for media center, spotlight
This commit is contained in:
@@ -17,7 +17,7 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
loadSettings()
|
||||
Qt.callLater(applyStoredTheme)
|
||||
// Theme will be applied after settings file is loaded in onLoaded handler
|
||||
}
|
||||
|
||||
Process {
|
||||
@@ -64,16 +64,22 @@ Singleton {
|
||||
(settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75
|
||||
recentlyUsedApps = settings.recentlyUsedApps || []
|
||||
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
|
||||
|
||||
// Apply the theme immediately after loading settings
|
||||
applyStoredTheme()
|
||||
} else {
|
||||
console.log("Settings file is empty")
|
||||
console.log("Settings file is empty - applying default theme")
|
||||
applyStoredTheme()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Could not parse settings, using defaults:", e)
|
||||
applyStoredTheme()
|
||||
}
|
||||
}
|
||||
|
||||
onLoadFailed: (error) => {
|
||||
console.log("Settings file not found, using defaults. Error:", error)
|
||||
applyStoredTheme()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,13 +105,15 @@ Singleton {
|
||||
}
|
||||
|
||||
function applyStoredTheme() {
|
||||
console.log("Applying stored theme:", themeIndex, themeIsDynamic)
|
||||
console.log("Applying stored theme:", themeIndex, themeIsDynamic, "lightMode:", isLightMode)
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.isLightMode = isLightMode
|
||||
Theme.switchTheme(themeIndex, themeIsDynamic, false)
|
||||
} else {
|
||||
Qt.callLater(() => {
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.isLightMode = isLightMode
|
||||
Theme.switchTheme(themeIndex, themeIsDynamic, false)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ QtObject {
|
||||
// Reference to the main shell root for calling functions
|
||||
property var rootObj: null
|
||||
|
||||
// Apply saved theme on startup
|
||||
// Initialize theme system
|
||||
Component.onCompleted: {
|
||||
console.log("Theme Component.onCompleted")
|
||||
|
||||
@@ -19,13 +19,7 @@ QtObject {
|
||||
Colors.colorsUpdated.connect(root.onColorsUpdated)
|
||||
}
|
||||
|
||||
Qt.callLater(() => {
|
||||
if (typeof Prefs !== "undefined") {
|
||||
console.log("Theme applying saved preferences:", Prefs.themeIndex, Prefs.themeIsDynamic, "lightMode:", Prefs.isLightMode)
|
||||
isLightMode = Prefs.isLightMode
|
||||
switchTheme(Prefs.themeIndex, Prefs.themeIsDynamic, false) // Don't save during startup
|
||||
}
|
||||
})
|
||||
console.log("Theme initialized, waiting for Prefs to load settings and apply theme")
|
||||
}
|
||||
|
||||
// Handle successful color extraction
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# System Monitor Widgets Usage Example
|
||||
|
||||
## Installation Complete
|
||||
|
||||
The CPU and RAM monitor widgets have been successfully created and integrated into your quickshell project:
|
||||
|
||||
### Files Created:
|
||||
- `/Widgets/CpuMonitorWidget.qml` - CPU usage monitor with progress bar and percentage
|
||||
- `/Widgets/RamMonitorWidget.qml` - RAM usage monitor with progress bar and percentage
|
||||
- `/Services/SystemMonitorService.qml` - Backend service for system monitoring
|
||||
|
||||
### Files Updated:
|
||||
- `/Widgets/qmldir` - Added widget exports
|
||||
- `/Services/qmldir` - Added service export
|
||||
|
||||
## Usage in TopBar
|
||||
|
||||
To add the system monitor widgets to your TopBar, add them to the right section alongside the BatteryWidget:
|
||||
|
||||
```qml
|
||||
// In TopBar.qml, around line 716 after BatteryWidget
|
||||
BatteryWidget {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// Add these new widgets:
|
||||
CpuMonitorWidget {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
showPercentage: true
|
||||
showIcon: true
|
||||
}
|
||||
|
||||
RamMonitorWidget {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
showPercentage: true
|
||||
showIcon: true
|
||||
}
|
||||
```
|
||||
|
||||
## Widget Features
|
||||
|
||||
### CpuMonitorWidget:
|
||||
- **Real-time CPU usage monitoring** (updates every 2 seconds)
|
||||
- **Visual progress bar** with color coding:
|
||||
- Green: < 60% usage
|
||||
- Orange: 60-80% usage
|
||||
- Red: > 80% usage
|
||||
- **Tooltip** showing CPU usage, core count, and frequency
|
||||
- **Material Design CPU icon** ()
|
||||
- **Configurable properties:**
|
||||
- `showPercentage: bool` - Show/hide percentage text
|
||||
- `showIcon: bool` - Show/hide CPU icon
|
||||
|
||||
### RamMonitorWidget:
|
||||
- **Real-time RAM usage monitoring** (updates every 3 seconds)
|
||||
- **Visual progress bar** with color coding:
|
||||
- Blue: < 75% usage
|
||||
- Orange: 75-90% usage
|
||||
- Red: > 90% usage
|
||||
- **Tooltip** showing memory usage, used/total memory in GB/MB
|
||||
- **Material Design memory icon** ()
|
||||
- **Configurable properties:**
|
||||
- `showPercentage: bool` - Show/hide percentage text
|
||||
- `showIcon: bool` - Show/hide RAM icon
|
||||
|
||||
### SystemMonitorService:
|
||||
- **Centralized system monitoring** backend service
|
||||
- **CPU monitoring:** usage, core count, frequency, temperature
|
||||
- **Memory monitoring:** usage percentage, total/used/free memory
|
||||
- **Automatic updates** with configurable intervals
|
||||
- **Helper functions** for formatting and color coding
|
||||
|
||||
## Widget Customization
|
||||
|
||||
Both widgets inherit your theme colors and styling:
|
||||
- Uses `Theme.cornerRadius` for rounded corners
|
||||
- Uses `Theme.primary/secondary` colors for progress bars
|
||||
- Uses `Theme.error/warning` for alert states
|
||||
- Uses `Theme.surfaceText` for text color
|
||||
- Consistent hover effects matching other widgets
|
||||
|
||||
## System Requirements
|
||||
|
||||
The widgets use standard Linux system commands:
|
||||
- `/proc/stat` for CPU usage
|
||||
- `/proc/meminfo` via `free` command for memory info
|
||||
- `/proc/cpuinfo` for CPU details
|
||||
- Works on most Linux distributions
|
||||
|
||||
The widgets are designed to integrate seamlessly with your existing quickshell material design theme and provide essential system monitoring information at a glance.
|
||||
@@ -136,4 +136,24 @@ Singleton {
|
||||
this.trackedPlayer = targetPlayer
|
||||
}
|
||||
|
||||
// Seeking support
|
||||
property bool canSeek: this.activePlayer?.canSeek ?? false
|
||||
property real position: this.activePlayer?.position ?? 0
|
||||
property real length: this.activePlayer?.length ?? 0
|
||||
|
||||
function seek(offsetUs) {
|
||||
if (this.canSeek && this.activePlayer) {
|
||||
this.activePlayer.seek(offsetUs)
|
||||
}
|
||||
}
|
||||
|
||||
function setPosition(trackId, positionUs) {
|
||||
if (this.canSeek && this.activePlayer && typeof this.activePlayer.setPosition === "function") {
|
||||
this.activePlayer.setPosition(trackId, positionUs)
|
||||
} else if (this.canSeek && this.activePlayer) {
|
||||
// Fallback to setting position property
|
||||
this.activePlayer.position = positionUs
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
# System Monitor Widget Improvements - Complete! ✅
|
||||
|
||||
## 🎯 **Issues Fixed:**
|
||||
|
||||
### **1. Icon Swap - DONE ✅**
|
||||
- **CPU Widget:** Now uses `memory` icon
|
||||
- **RAM Widget:** Now uses `developer_board` icon
|
||||
|
||||
### **2. Percentage Values Working - DONE ✅**
|
||||
- Both widgets now display real-time percentages correctly
|
||||
- Service is properly collecting system data
|
||||
|
||||
### **3. Vertical Alignment - FIXED ✅**
|
||||
- Added `anchors.verticalCenter: parent.verticalCenter` to both icon and percentage text
|
||||
- Icons and percentages now properly align within the widget container
|
||||
|
||||
### **4. Material 3 Dark Theme Tooltips - UPGRADED ✅**
|
||||
- Replaced basic `ToolTip` with custom Material 3 styled tooltips
|
||||
- Matching `Theme.surfaceContainer` background
|
||||
- Proper `Theme.outline` borders with opacity
|
||||
- Smooth fade animations with `Theme.shortDuration`
|
||||
- Better text spacing and alignment
|
||||
- Wider tooltips to prevent text cutoff
|
||||
|
||||
## 🎨 **New Tooltip Features:**
|
||||
|
||||
### **CPU Tooltip:**
|
||||
```
|
||||
CPU Usage: X.X%
|
||||
Cores: N
|
||||
Frequency: X.X GHz
|
||||
```
|
||||
|
||||
### **RAM Tooltip:**
|
||||
```
|
||||
Memory Usage: X.X%
|
||||
Used: X.X GB
|
||||
Total: X.X GB
|
||||
```
|
||||
|
||||
## 📱 **Final Result:**
|
||||
- **CPU Widget:** `memory 7%` with beautiful Material 3 tooltip
|
||||
- **RAM Widget:** `developer_board 67%` with beautiful Material 3 tooltip
|
||||
- Perfect vertical alignment of icons and text
|
||||
- Smooth hover animations
|
||||
- Professional dark theme styling
|
||||
- No more cutoff tooltip text
|
||||
|
||||
The widgets are now production-ready with a polished Material 3 Dark expressive theme that matches your existing quickshell design language!
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -639,10 +639,13 @@ PanelWindow {
|
||||
z: -1
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow {
|
||||
radius: 8
|
||||
samples: 16
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 0
|
||||
shadowBlur: 0.25 // radius/32
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.2)
|
||||
shadowOpacity: 0.2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Services.Mpris
|
||||
import "../../Common"
|
||||
@@ -19,103 +19,32 @@ Rectangle {
|
||||
border.color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.2)
|
||||
border.width: 1
|
||||
|
||||
// Constants and helpers - all microseconds
|
||||
readonly property real oneSecondUs: 1000000.0
|
||||
property real currentPosition: 0
|
||||
|
||||
function asSec(us) { return us / oneSecondUs }
|
||||
function ratio() { return trackLenUs > 0 ? uiPosUs / trackLenUs : 0 }
|
||||
|
||||
function normalizeLength(lenRaw) {
|
||||
// If length < 86 400 it's almost certainly seconds (24 h upper bound).
|
||||
// Convert to µs; otherwise return as-is.
|
||||
return (lenRaw > 0 && lenRaw < 86400) ? lenRaw * oneSecondUs : lenRaw;
|
||||
// Simple progress ratio calculation
|
||||
function ratio() {
|
||||
return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0
|
||||
}
|
||||
|
||||
|
||||
// Call seek() in safe 5-second chunks so every player obeys.
|
||||
function chunkedSeek(offsetUs) {
|
||||
if (Math.abs(offsetUs) < 5 * oneSecondUs) { // ≤5 s? single shot.
|
||||
activePlayer.seek(offsetUs);
|
||||
return;
|
||||
}
|
||||
|
||||
const step = 5 * oneSecondUs; // 5 s
|
||||
let remaining = offsetUs;
|
||||
let safety = 0; // avoid infinite loops
|
||||
while (Math.abs(remaining) > step && safety < 40) { // max 200 s
|
||||
activePlayer.seek(Math.sign(remaining) * step);
|
||||
remaining -= Math.sign(remaining) * step;
|
||||
safety++;
|
||||
}
|
||||
if (remaining !== 0) activePlayer.seek(remaining);
|
||||
}
|
||||
|
||||
// Returns a guaranteed-valid object-path for the current track.
|
||||
function trackPath() {
|
||||
const md = activePlayer.metadata || {};
|
||||
// Spec: "/org/mpris/MediaPlayer2/Track/NNN"
|
||||
if (typeof md["mpris:trackid"] === "string" &&
|
||||
md["mpris:trackid"].length > 1 && md["mpris:trackid"].startsWith("/"))
|
||||
return md["mpris:trackid"];
|
||||
|
||||
// Nothing reliable? Fall back to the *current* playlist entry object if exposed
|
||||
if (activePlayer.currentTrackPath) return activePlayer.currentTrackPath;
|
||||
|
||||
// Absolute last resort—return null so caller knows SetPosition will fail
|
||||
return null;
|
||||
}
|
||||
|
||||
// Position tracking - all microseconds
|
||||
property real uiPosUs: 0
|
||||
property real backendPosUs: 0
|
||||
property real trackLenUs: 0
|
||||
property double backendStamp: Date.now() // wall-clock of last update in ms
|
||||
|
||||
// Optimistic timer
|
||||
// Updates progress bar every second
|
||||
Timer {
|
||||
id: tickTimer
|
||||
interval: 50 // 20 fps feels smooth, cheap
|
||||
id: positionTimer
|
||||
interval: 1000
|
||||
running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking
|
||||
repeat: true
|
||||
running: activePlayer?.playbackState === MprisPlaybackState.Playing
|
||||
onTriggered: {
|
||||
if (trackLenUs <= 0) return;
|
||||
const projected = backendPosUs + (Date.now() - backendStamp) * 1000.0;
|
||||
uiPosUs = Math.min(projected, trackLenUs); // never exceed track end
|
||||
if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
|
||||
currentPosition = activePlayer.position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 500-ms poll to keep external moves in sync -------------------
|
||||
// Timer {
|
||||
// id: pollTimer
|
||||
// interval: 500 // ms
|
||||
// repeat: true
|
||||
// running: true // always on; cost is negligible
|
||||
// onTriggered: {
|
||||
// if (!activePlayer || trackLenUs <= 0) return;
|
||||
|
||||
// const polledUs = activePlayer.position; // property read
|
||||
// // Compare in percent to avoid false positives
|
||||
// if (Math.abs((polledUs - backendPosUs) / trackLenUs) > 0.01) { // >1 % jump
|
||||
// backendPosUs = polledUs;
|
||||
// backendStamp = Date.now();
|
||||
// uiPosUs = polledUs; // snap instantly
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Initialize when player changes
|
||||
onActivePlayerChanged: {
|
||||
if (activePlayer) {
|
||||
backendPosUs = activePlayer.position || 0
|
||||
trackLenUs = normalizeLength(activePlayer.length || 0)
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = backendPosUs
|
||||
console.log(`player change → len ${asSec(trackLenUs)} s, pos ${asSec(uiPosUs)} s`)
|
||||
currentPosition = activePlayer.position || 0
|
||||
} else {
|
||||
backendPosUs = 0
|
||||
trackLenUs = 0
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = 0
|
||||
currentPosition = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,30 +53,17 @@ Rectangle {
|
||||
target: activePlayer
|
||||
|
||||
function onPositionChanged() {
|
||||
const posUs = activePlayer.position
|
||||
backendPosUs = posUs
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = posUs // snap immediately on tick
|
||||
}
|
||||
|
||||
function onSeeked(pos) {
|
||||
backendPosUs = pos
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = backendPosUs
|
||||
if (!progressMouseArea.isSeeking) {
|
||||
currentPosition = activePlayer.position
|
||||
}
|
||||
}
|
||||
|
||||
function onPostTrackChanged() {
|
||||
backendPosUs = activePlayer?.position || 0
|
||||
trackLenUs = normalizeLength(activePlayer?.length || 0)
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = backendPosUs
|
||||
currentPosition = activePlayer?.position || 0
|
||||
}
|
||||
|
||||
function onTrackTitleChanged() {
|
||||
backendPosUs = activePlayer?.position || 0
|
||||
trackLenUs = normalizeLength(activePlayer?.length || 0)
|
||||
backendStamp = Date.now()
|
||||
uiPosUs = backendPosUs
|
||||
currentPosition = activePlayer?.position || 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +149,7 @@ Rectangle {
|
||||
|
||||
// Progress bar
|
||||
Rectangle {
|
||||
id: progressBarBackground
|
||||
width: parent.width
|
||||
height: 6
|
||||
radius: 3
|
||||
@@ -245,27 +162,72 @@ Rectangle {
|
||||
color: theme.primary
|
||||
|
||||
width: parent.width * ratio()
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation { duration: 100 }
|
||||
}
|
||||
}
|
||||
|
||||
// Drag handle
|
||||
Rectangle {
|
||||
id: progressHandle
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: theme.primary
|
||||
border.color: Qt.lighter(theme.primary, 1.3)
|
||||
border.width: 1
|
||||
|
||||
x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2))
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
visible: activePlayer && activePlayer.length > 0
|
||||
scale: progressMouseArea.containsMouse || progressMouseArea.pressed ? 1.2 : 1.0
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 150 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: progressMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: activePlayer && activePlayer.length > 0 && activePlayer.canSeek
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (!activePlayer || !activePlayer.canSeek || trackLenUs <= 0) return
|
||||
|
||||
const targetUs = (mouse.x / width) * trackLenUs
|
||||
const offset = targetUs - backendPosUs
|
||||
|
||||
if (typeof activePlayer.setPosition === "function") {
|
||||
activePlayer.setPosition(trackPath() || "/", Math.round(targetUs))
|
||||
console.log(`SetPosition → ${asSec(targetUs)} s`)
|
||||
} else {
|
||||
chunkedSeek(offset) // <-- use helper
|
||||
console.log(`chunkedSeek → ${asSec(offset/oneSecondUs)} s`)
|
||||
property bool isSeeking: false
|
||||
|
||||
onClicked: function(mouse) {
|
||||
if (activePlayer && activePlayer.length > 0) {
|
||||
let ratio = mouse.x / width
|
||||
let seekPosition = ratio * activePlayer.length
|
||||
activePlayer.position = seekPosition
|
||||
currentPosition = seekPosition
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: function(mouse) {
|
||||
isSeeking = true
|
||||
if (activePlayer && activePlayer.length > 0) {
|
||||
let ratio = Math.max(0, Math.min(1, mouse.x / width))
|
||||
let seekPosition = ratio * activePlayer.length
|
||||
activePlayer.position = seekPosition
|
||||
currentPosition = seekPosition
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
isSeeking = false
|
||||
}
|
||||
|
||||
onPositionChanged: function(mouse) {
|
||||
if (pressed && activePlayer && activePlayer.length > 0) {
|
||||
let ratio = Math.max(0, Math.min(1, mouse.x / width))
|
||||
let seekPosition = ratio * activePlayer.length
|
||||
activePlayer.position = seekPosition
|
||||
currentPosition = seekPosition
|
||||
}
|
||||
|
||||
uiPosUs = backendPosUs = targetUs
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,15 +261,9 @@ Rectangle {
|
||||
if (!activePlayer) return
|
||||
|
||||
// >8 s → jump to start, otherwise previous track
|
||||
if (uiPosUs > 8 * oneSecondUs && activePlayer.canSeek) {
|
||||
if (typeof activePlayer.setPosition === "function") {
|
||||
activePlayer.setPosition(trackPath() || "/", 0)
|
||||
console.log("Back → SetPosition 0 µs")
|
||||
} else {
|
||||
chunkedSeek(-backendPosUs) // <-- use helper
|
||||
console.log("Back → chunkedSeek to 0")
|
||||
}
|
||||
uiPosUs = backendPosUs = 0
|
||||
if (currentPosition > 8 && activePlayer.canSeek) {
|
||||
activePlayer.position = 0
|
||||
currentPosition = 0
|
||||
} else {
|
||||
activePlayer.previous()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -230,7 +230,8 @@ PanelWindow {
|
||||
sourceSize.height: size
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskSource: Rectangle {
|
||||
width: historyNotifImage.size
|
||||
height: historyNotifImage.size
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -179,7 +179,8 @@ PanelWindow {
|
||||
sourceSize.height: size
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskSource: Rectangle {
|
||||
width: notifImage.size
|
||||
height: notifImage.size
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -124,17 +124,18 @@ PanelWindow {
|
||||
}
|
||||
} else {
|
||||
// Search with category filter
|
||||
var baseApps = selectedCategory === "All" ?
|
||||
AppSearchService.applications :
|
||||
AppSearchService.getAppsInCategory(selectedCategory)
|
||||
var searchResults = AppSearchService.searchApplications(searchField.text)
|
||||
|
||||
if (selectedCategory === "All") {
|
||||
// For "All" category, show all search results without limit
|
||||
apps = searchResults.filter(app => baseApps.includes(app))
|
||||
// For "All" category, search all apps without limit
|
||||
apps = AppSearchService.searchApplications(searchField.text)
|
||||
} else {
|
||||
// For specific categories, still apply limit
|
||||
apps = searchResults.filter(app => baseApps.includes(app)).slice(0, maxResults)
|
||||
// For specific categories, filter search results by category
|
||||
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory)
|
||||
var allSearchResults = AppSearchService.searchApplications(searchField.text)
|
||||
|
||||
// Filter search results to only include apps from the selected category
|
||||
apps = allSearchResults.filter(searchApp => {
|
||||
return categoryApps.some(categoryApp => categoryApp.name === searchApp.name)
|
||||
}).slice(0, maxResults)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +241,14 @@ PanelWindow {
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 1
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow { radius: 32; samples: 64; color: Qt.rgba(0,0,0,0.3); horizontalOffset:0; verticalOffset:8 }
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 8
|
||||
shadowBlur: 1.0 // radius/32
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||
shadowOpacity: 0.3
|
||||
}
|
||||
transform: Scale { origin.x: width/2; origin.y: height/2; xScale: spotlightOpen?1:0.9; yScale: spotlightOpen?1:0.9;
|
||||
Behavior on xScale { NumberAnimation { duration: Theme.mediumDuration; easing.type: Easing.OutBack; easing.overshoot:1.1 } }
|
||||
Behavior on yScale { NumberAnimation { duration: Theme.mediumDuration; easing.type: Easing.OutBack; easing.overshoot:1.1 } }
|
||||
@@ -379,8 +387,8 @@ PanelWindow {
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
focus: spotlightOpen
|
||||
selectByMouse: true
|
||||
onTextChanged: updateFilteredApps
|
||||
Keys.onPressed: {
|
||||
onTextChanged: updateFilteredApps()
|
||||
Keys.onPressed: (event) => {
|
||||
if(event.key === Qt.Key_Escape) { hide(); event.accepted = true }
|
||||
else if(event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { launchSelected(); event.accepted = true }
|
||||
else if(event.key === Qt.Key_Down) { selectNext(); event.accepted = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -44,15 +44,12 @@ PanelWindow {
|
||||
property bool bluetoothAvailable: false
|
||||
property bool bluetoothEnabled: false
|
||||
|
||||
// Shell reference to access root properties directly
|
||||
property var shellRoot: null
|
||||
|
||||
// Notification properties
|
||||
property bool notificationHistoryVisible: false
|
||||
property int notificationCount: 0
|
||||
|
||||
// Control center properties
|
||||
property bool controlCenterVisible: false
|
||||
|
||||
// Calendar properties
|
||||
property bool calendarVisible: false
|
||||
|
||||
// Clipboard properties
|
||||
signal clipboardRequested()
|
||||
@@ -64,6 +61,7 @@ PanelWindow {
|
||||
property real trayMenuX: 0
|
||||
property real trayMenuY: 0
|
||||
|
||||
|
||||
// Proxy objects for external connections
|
||||
|
||||
QtObject {
|
||||
@@ -107,13 +105,13 @@ PanelWindow {
|
||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, topBar.backgroundTransparency)
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow {
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 4
|
||||
radius: 16
|
||||
samples: 33
|
||||
color: Qt.rgba(0, 0, 0, 0.15)
|
||||
transparentBorder: true
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 4
|
||||
shadowBlur: 0.5 // radius/32, adjusted for visual match
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.15)
|
||||
shadowOpacity: 0.15
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -183,7 +181,9 @@ PanelWindow {
|
||||
useFahrenheit: topBar.useFahrenheit
|
||||
|
||||
onClockClicked: {
|
||||
topBar.calendarVisible = !topBar.calendarVisible
|
||||
if (topBar.shellRoot) {
|
||||
topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible
|
||||
}
|
||||
}
|
||||
|
||||
// Insert audio visualization into the clock widget placeholder
|
||||
@@ -263,9 +263,11 @@ PanelWindow {
|
||||
NotificationCenterButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
hasUnread: topBar.notificationCount > 0
|
||||
isActive: topBar.notificationHistoryVisible
|
||||
isActive: topBar.shellRoot ? topBar.shellRoot.notificationHistoryVisible : false
|
||||
onClicked: {
|
||||
topBar.notificationHistoryVisible = !topBar.notificationHistoryVisible
|
||||
if (topBar.shellRoot) {
|
||||
topBar.shellRoot.notificationHistoryVisible = !topBar.shellRoot.notificationHistoryVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,13 +283,15 @@ PanelWindow {
|
||||
volumeLevel: topBar.volumeLevel
|
||||
bluetoothAvailable: topBar.bluetoothAvailable
|
||||
bluetoothEnabled: topBar.bluetoothEnabled
|
||||
isActive: topBar.controlCenterVisible
|
||||
isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false
|
||||
|
||||
onClicked: {
|
||||
topBar.controlCenterVisible = !topBar.controlCenterVisible
|
||||
if (topBar.controlCenterVisible) {
|
||||
WifiService.scanWifi()
|
||||
BluetoothService.scanDevices()
|
||||
if (topBar.shellRoot) {
|
||||
topBar.shellRoot.controlCenterVisible = !topBar.shellRoot.controlCenterVisible
|
||||
if (topBar.shellRoot.controlCenterVisible) {
|
||||
WifiService.scanWifi()
|
||||
BluetoothService.scanDevices()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
shell.qml
11
shell.qml
@@ -2,7 +2,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
@@ -298,10 +298,8 @@ ShellRoot {
|
||||
volumeLevel: root.volumeLevel
|
||||
bluetoothAvailable: root.bluetoothAvailable
|
||||
bluetoothEnabled: root.bluetoothEnabled
|
||||
notificationHistoryVisible: root.notificationHistoryVisible
|
||||
shellRoot: root
|
||||
notificationCount: notificationHistory.count
|
||||
controlCenterVisible: root.controlCenterVisible
|
||||
calendarVisible: root.calendarVisible
|
||||
|
||||
// Connect tray menu properties
|
||||
showTrayMenu: root.showTrayMenu
|
||||
@@ -315,10 +313,7 @@ ShellRoot {
|
||||
clipboardHistoryPopup.toggle()
|
||||
}
|
||||
|
||||
// Property change handlers
|
||||
onCalendarVisibleChanged: root.calendarVisible = calendarVisible
|
||||
onControlCenterVisibleChanged: root.controlCenterVisible = controlCenterVisible
|
||||
onNotificationHistoryVisibleChanged: root.notificationHistoryVisible = notificationHistoryVisible
|
||||
|
||||
onShowTrayMenuChanged: root.showTrayMenu = showTrayMenu
|
||||
onCurrentTrayMenuChanged: root.currentTrayMenu = currentTrayMenu
|
||||
onCurrentTrayItemChanged: root.currentTrayItem = currentTrayItem
|
||||
|
||||
Reference in New Issue
Block a user