mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 16:02:51 -05:00
Initial qmlformat
This commit is contained in:
23
CLAUDE.md
23
CLAUDE.md
@@ -25,6 +25,10 @@ quickshell -p shell.qml
|
|||||||
|
|
||||||
# Or use the shorthand
|
# Or use the shorthand
|
||||||
qs -p .
|
qs -p .
|
||||||
|
|
||||||
|
# Code formatting and linting
|
||||||
|
qmlformat -i **/*.qml # Format all QML files in place
|
||||||
|
qmllint **/*.qml # Lint all QML files for syntax errors
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
@@ -119,13 +123,19 @@ shell.qml # Main entry point (minimal orchestration)
|
|||||||
- `id` should be the first property
|
- `id` should be the first property
|
||||||
- Properties before signal handlers before child components
|
- Properties before signal handlers before child components
|
||||||
- Prefer property bindings over imperative code
|
- Prefer property bindings over imperative code
|
||||||
|
- **IMPORTANT**: Be very conservative with comments - add comments only when absolutely necessary for understanding complex logic
|
||||||
|
|
||||||
2. **Naming Conventions**:
|
2. **Naming Conventions**:
|
||||||
- **Services**: Use `Singleton` type with `id: root`
|
- **Services**: Use `Singleton` type with `id: root`
|
||||||
- **Components**: Use descriptive names (e.g., `CustomSlider`, `TopBar`)
|
- **Components**: Use descriptive names (e.g., `CustomSlider`, `TopBar`)
|
||||||
- **Properties**: camelCase for properties, PascalCase for types
|
- **Properties**: camelCase for properties, PascalCase for types
|
||||||
|
|
||||||
3. **Component Structure**:
|
3. **Null-Safe Operations**:
|
||||||
|
- **Do NOT use** `?.` operator (not supported by qmlformat)
|
||||||
|
- **Use** `object && object.property` instead of `object?.property`
|
||||||
|
- **Example**: `activePlayer && activePlayer.trackTitle` instead of `activePlayer?.trackTitle`
|
||||||
|
|
||||||
|
4. **Component Structure**:
|
||||||
```qml
|
```qml
|
||||||
// For regular components
|
// For regular components
|
||||||
Item {
|
Item {
|
||||||
@@ -230,11 +240,12 @@ The shell uses Quickshell's `Variants` pattern for multi-monitor support:
|
|||||||
|
|
||||||
When modifying the shell:
|
When modifying the shell:
|
||||||
1. **Test changes**: `qs -p .` (automatic reload on file changes)
|
1. **Test changes**: `qs -p .` (automatic reload on file changes)
|
||||||
2. **Performance**: Ensure animations remain smooth (60 FPS target)
|
2. **Code quality**: Run `qmlformat -i **/*.qml` and `qmllint **/*.qml` to ensure proper formatting and syntax
|
||||||
3. **Theming**: Use `Theme.propertyName` for Material Design 3 consistency
|
3. **Performance**: Ensure animations remain smooth (60 FPS target)
|
||||||
4. **Wayland compatibility**: Test on Wayland session
|
4. **Theming**: Use `Theme.propertyName` for Material Design 3 consistency
|
||||||
5. **Multi-monitor**: Verify behavior with multiple displays
|
5. **Wayland compatibility**: Test on Wayland session
|
||||||
6. **Feature detection**: Test on systems with/without required tools
|
6. **Multi-monitor**: Verify behavior with multiple displays
|
||||||
|
7. **Feature detection**: Test on systems with/without required tools
|
||||||
|
|
||||||
### Adding New Widgets
|
### Adding New Widgets
|
||||||
|
|
||||||
|
|||||||
@@ -1,180 +1,181 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Qt.labs.platform
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Qt.labs.platform
|
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
|
// This dependency forces re-evaluation when colorUpdateTrigger changes
|
||||||
|
// Just check if matugen is available
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
/* ──────────────── basic state ──────────────── */
|
|
||||||
signal colorsUpdated()
|
|
||||||
|
|
||||||
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||||
readonly property string homeDir: _homeUrl.startsWith("file://")
|
readonly property string homeDir: _homeUrl.startsWith("file://") ? _homeUrl.substring(7) : _homeUrl
|
||||||
? _homeUrl.substring(7)
|
|
||||||
: _homeUrl
|
|
||||||
readonly property string wallpaperPath: homeDir + "/quickshell/current_wallpaper"
|
readonly property string wallpaperPath: homeDir + "/quickshell/current_wallpaper"
|
||||||
readonly property string notifyPath: homeDir + "/quickshell/wallpaper_changed"
|
readonly property string notifyPath: homeDir + "/quickshell/wallpaper_changed"
|
||||||
|
property bool matugenAvailable: false
|
||||||
|
property string matugenJson: ""
|
||||||
|
property var matugenColors: ({
|
||||||
|
})
|
||||||
|
property bool extractionRequested: false
|
||||||
|
property int colorUpdateTrigger: 0 // Force property re-evaluation
|
||||||
|
// ──────────────── wallpaper change monitor ────────────────
|
||||||
|
property string lastWallpaperTimestamp: ""
|
||||||
|
// ──────────────── color properties (MD3) ────────────────
|
||||||
|
property color primary: getMatugenColor("primary", "#42a5f5")
|
||||||
|
property color secondary: getMatugenColor("secondary", "#8ab4f8")
|
||||||
|
property color tertiary: getMatugenColor("tertiary", "#bb86fc")
|
||||||
|
property color tertiaryContainer: getMatugenColor("tertiary_container", "#3700b3")
|
||||||
|
property color error: getMatugenColor("error", "#cf6679")
|
||||||
|
property color inversePrimary: getMatugenColor("inverse_primary", "#6200ea")
|
||||||
|
// backgrounds
|
||||||
|
property color bg: getMatugenColor("background", "#1a1c1e")
|
||||||
|
property color surface: getMatugenColor("surface", "#1a1c1e")
|
||||||
|
property color surfaceContainer: getMatugenColor("surface_container", "#1e2023")
|
||||||
|
property color surfaceContainerHigh: getMatugenColor("surface_container_high", "#292b2f")
|
||||||
|
property color surfaceVariant: getMatugenColor("surface_variant", "#44464f")
|
||||||
|
// text
|
||||||
|
property color surfaceText: getMatugenColor("on_background", "#e3e8ef")
|
||||||
|
property color primaryText: getMatugenColor("on_primary", "#ffffff")
|
||||||
|
property color surfaceVariantText: getMatugenColor("on_surface_variant", "#c4c7c5")
|
||||||
|
// containers & misc
|
||||||
|
property color primaryContainer: getMatugenColor("primary_container", "#1976d2")
|
||||||
|
property color surfaceTint: getMatugenColor("surface_tint", "#8ab4f8")
|
||||||
|
property color outline: getMatugenColor("outline", "#8e918f")
|
||||||
|
// legacy aliases
|
||||||
|
property color accentHi: primary
|
||||||
|
property color accentLo: secondary
|
||||||
|
|
||||||
property bool matugenAvailable: false
|
// ──────────────── basic state ────────────────
|
||||||
property string matugenJson: ""
|
signal colorsUpdated()
|
||||||
property var matugenColors: ({})
|
|
||||||
property bool extractionRequested: false
|
|
||||||
property int colorUpdateTrigger: 0 // Force property re-evaluation
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
console.log("Colors.qml → home =", homeDir)
|
|
||||||
// Don't automatically run color extraction - only when requested
|
|
||||||
matugenCheck.running = true // Just check if matugen is available
|
|
||||||
|
|
||||||
// Connect to Theme light mode changes to update colors
|
|
||||||
if (typeof Theme !== "undefined") {
|
|
||||||
Theme.isLightModeChanged.connect(root.onLightModeChanged)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLightModeChanged() {
|
function onLightModeChanged() {
|
||||||
// Force color properties to update when light mode changes
|
// Force color properties to update when light mode changes
|
||||||
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
||||||
console.log("Light mode changed - updating dynamic colors")
|
console.log("Light mode changed - updating dynamic colors");
|
||||||
colorUpdateTrigger++ // This will trigger re-evaluation of all color properties
|
colorUpdateTrigger++; // This will trigger re-evaluation of all color properties
|
||||||
colorsUpdated()
|
colorsUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ──────────────── availability checks ──────────────── */
|
// ──────────────── public helper ────────────────
|
||||||
Process {
|
|
||||||
id: matugenCheck
|
|
||||||
command: ["which", "matugen"]
|
|
||||||
onExited: (code) => {
|
|
||||||
matugenAvailable = (code === 0)
|
|
||||||
console.log("Matugen in PATH:", matugenAvailable)
|
|
||||||
|
|
||||||
if (!matugenAvailable) {
|
|
||||||
console.warn("Matugen missing → dynamic theme disabled")
|
|
||||||
ToastService.wallpaperErrorStatus = "matugen_missing"
|
|
||||||
ToastService.showWarning("matugen not found - dynamic theming disabled")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If extraction was requested, continue the process
|
|
||||||
if (extractionRequested) {
|
|
||||||
console.log("Continuing with color extraction")
|
|
||||||
fileChecker.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: fileChecker // exists & readable?
|
|
||||||
command: ["test", "-r", wallpaperPath]
|
|
||||||
onExited: (code) => {
|
|
||||||
if (code === 0) {
|
|
||||||
matugenProcess.running = true
|
|
||||||
} else {
|
|
||||||
console.error("code", code)
|
|
||||||
console.error("Wallpaper not found:", wallpaperPath)
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper processing failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ──────────────── matugen invocation ──────────────── */
|
|
||||||
Process {
|
|
||||||
id: matugenProcess
|
|
||||||
command: ["matugen", "-v", "image", wallpaperPath, "--json", "hex"]
|
|
||||||
|
|
||||||
/* ── grab stdout as a stream ── */
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: matugenCollector
|
|
||||||
onStreamFinished: {
|
|
||||||
const out = matugenCollector.text
|
|
||||||
if (!out.length) {
|
|
||||||
console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr)
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper processing failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
root.matugenJson = out
|
|
||||||
root.matugenColors = JSON.parse(out)
|
|
||||||
root.colorsUpdated()
|
|
||||||
ToastService.clearWallpaperError()
|
|
||||||
ToastService.showInfo("Dynamic theme colors updated")
|
|
||||||
} catch (e) {
|
|
||||||
console.error("JSON parse failed:", e)
|
|
||||||
ToastService.wallpaperErrorStatus = "error"
|
|
||||||
ToastService.showError("Wallpaper processing failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* grab stderr too, so we can print it above */
|
|
||||||
stderr: StdioCollector { id: matugenErr }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ──────────────── wallpaper change monitor ──────────────── */
|
|
||||||
property string lastWallpaperTimestamp: ""
|
|
||||||
|
|
||||||
|
|
||||||
/* ──────────────── public helper ──────────────── */
|
|
||||||
function extractColors() {
|
function extractColors() {
|
||||||
console.log("Colors.extractColors() called, matugenAvailable:", matugenAvailable)
|
console.log("Colors.extractColors() called, matugenAvailable:", matugenAvailable);
|
||||||
extractionRequested = true
|
extractionRequested = true;
|
||||||
if (matugenAvailable)
|
if (matugenAvailable)
|
||||||
fileChecker.running = true
|
fileChecker.running = true;
|
||||||
else
|
else
|
||||||
matugenCheck.running = true
|
matugenCheck.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMatugenColor(path, fallback) {
|
function getMatugenColor(path, fallback) {
|
||||||
// Include colorUpdateTrigger in the function to make properties reactive to changes
|
// Include colorUpdateTrigger in the function to make properties reactive to changes
|
||||||
colorUpdateTrigger // This dependency forces re-evaluation when colorUpdateTrigger changes
|
colorUpdateTrigger;
|
||||||
|
|
||||||
// Use light or dark colors based on Theme.isLightMode
|
// Use light or dark colors based on Theme.isLightMode
|
||||||
const colorMode = (typeof Theme !== "undefined" && Theme.isLightMode) ? "light" : "dark"
|
const colorMode = (typeof Theme !== "undefined" && Theme.isLightMode) ? "light" : "dark";
|
||||||
let cur = matugenColors?.colors?.[colorMode]
|
let cur = matugenColors && matugenColors.colors && matugenColors.colors[colorMode];
|
||||||
for (const part of path.split(".")) {
|
for (const part of path.split(".")) {
|
||||||
if (!cur || typeof cur !== "object" || !(part in cur))
|
if (!cur || typeof cur !== "object" || !(part in cur))
|
||||||
return fallback
|
return fallback;
|
||||||
cur = cur[part]
|
|
||||||
|
cur = cur[part];
|
||||||
}
|
}
|
||||||
return cur || fallback
|
return cur || fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ──────────────── color properties (MD3) ──────────────── */
|
|
||||||
property color primary: getMatugenColor("primary", "#42a5f5")
|
|
||||||
property color secondary: getMatugenColor("secondary", "#8ab4f8")
|
|
||||||
property color tertiary: getMatugenColor("tertiary", "#bb86fc")
|
|
||||||
property color tertiaryContainer: getMatugenColor("tertiary_container", "#3700b3")
|
|
||||||
property color error: getMatugenColor("error", "#cf6679")
|
|
||||||
property color inversePrimary: getMatugenColor("inverse_primary", "#6200ea")
|
|
||||||
|
|
||||||
/* backgrounds */
|
|
||||||
property color bg: getMatugenColor("background", "#1a1c1e")
|
|
||||||
property color surface: getMatugenColor("surface", "#1a1c1e")
|
|
||||||
property color surfaceContainer: getMatugenColor("surface_container", "#1e2023")
|
|
||||||
property color surfaceContainerHigh: getMatugenColor("surface_container_high", "#292b2f")
|
|
||||||
property color surfaceVariant: getMatugenColor("surface_variant", "#44464f")
|
|
||||||
|
|
||||||
/* text */
|
|
||||||
property color surfaceText: getMatugenColor("on_background", "#e3e8ef")
|
|
||||||
property color primaryText: getMatugenColor("on_primary", "#ffffff")
|
|
||||||
property color surfaceVariantText: getMatugenColor("on_surface_variant", "#c4c7c5")
|
|
||||||
|
|
||||||
/* containers & misc */
|
|
||||||
property color primaryContainer: getMatugenColor("primary_container", "#1976d2")
|
|
||||||
property color surfaceTint: getMatugenColor("surface_tint", "#8ab4f8")
|
|
||||||
property color outline: getMatugenColor("outline", "#8e918f")
|
|
||||||
|
|
||||||
/* legacy aliases */
|
|
||||||
property color accentHi: primary
|
|
||||||
property color accentLo: secondary
|
|
||||||
|
|
||||||
function isColorDark(c) {
|
function isColorDark(c) {
|
||||||
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) < 0.5
|
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) < 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("Colors.qml → home =", homeDir);
|
||||||
|
// Don't automatically run color extraction - only when requested
|
||||||
|
matugenCheck.running = true;
|
||||||
|
// Connect to Theme light mode changes to update colors
|
||||||
|
if (typeof Theme !== "undefined")
|
||||||
|
Theme.isLightModeChanged.connect(root.onLightModeChanged);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ──────────────── availability checks ────────────────
|
||||||
|
Process {
|
||||||
|
id: matugenCheck
|
||||||
|
|
||||||
|
command: ["which", "matugen"]
|
||||||
|
onExited: (code) => {
|
||||||
|
matugenAvailable = (code === 0);
|
||||||
|
console.log("Matugen in PATH:", matugenAvailable);
|
||||||
|
if (!matugenAvailable) {
|
||||||
|
console.warn("Matugen missing → dynamic theme disabled");
|
||||||
|
ToastService.wallpaperErrorStatus = "matugen_missing";
|
||||||
|
ToastService.showWarning("matugen not found - dynamic theming disabled");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
// If extraction was requested, continue the process
|
||||||
|
if (extractionRequested) {
|
||||||
|
console.log("Continuing with color extraction");
|
||||||
|
fileChecker.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: fileChecker // exists & readable?
|
||||||
|
|
||||||
|
command: ["test", "-r", wallpaperPath]
|
||||||
|
onExited: (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
matugenProcess.running = true;
|
||||||
|
} else {
|
||||||
|
console.error("code", code);
|
||||||
|
console.error("Wallpaper not found:", wallpaperPath);
|
||||||
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
|
ToastService.showError("Wallpaper processing failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ──────────────── matugen invocation ────────────────
|
||||||
|
Process {
|
||||||
|
id: matugenProcess
|
||||||
|
|
||||||
|
command: ["matugen", "-v", "image", wallpaperPath, "--json", "hex"]
|
||||||
|
|
||||||
|
// ── grab stdout as a stream ──
|
||||||
|
stdout: StdioCollector {
|
||||||
|
id: matugenCollector
|
||||||
|
|
||||||
|
onStreamFinished: {
|
||||||
|
const out = matugenCollector.text;
|
||||||
|
if (!out.length) {
|
||||||
|
console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr);
|
||||||
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
|
ToastService.showError("Wallpaper Processing Failed");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
root.matugenJson = out;
|
||||||
|
root.matugenColors = JSON.parse(out);
|
||||||
|
root.colorsUpdated();
|
||||||
|
ToastService.clearWallpaperError();
|
||||||
|
ToastService.showInfo("Loaded Dynamic Theme Colors");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("JSON parse failed:", e);
|
||||||
|
ToastService.wallpaperErrorStatus = "error";
|
||||||
|
ToastService.showError("Wallpaper Processing Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab stderr too, so we can print it above
|
||||||
|
stderr: StdioCollector {
|
||||||
|
id: matugenErr
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
355
Common/Prefs.qml
355
Common/Prefs.qml
@@ -1,10 +1,15 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
import QtQuick
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtCore
|
import QtCore
|
||||||
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
|
// "auto", "wifi", "ethernet"
|
||||||
|
// Alphabetical tiebreaker
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int themeIndex: 0
|
property int themeIndex: 0
|
||||||
@@ -13,14 +18,12 @@ Singleton {
|
|||||||
property real topBarTransparency: 0.75
|
property real topBarTransparency: 0.75
|
||||||
property real popupTransparency: 0.92
|
property real popupTransparency: 0.92
|
||||||
property var recentlyUsedApps: []
|
property var recentlyUsedApps: []
|
||||||
|
|
||||||
// New global preferences
|
// New global preferences
|
||||||
property bool use24HourClock: true
|
property bool use24HourClock: true
|
||||||
property bool useFahrenheit: false
|
property bool useFahrenheit: false
|
||||||
property bool nightModeEnabled: false
|
property bool nightModeEnabled: false
|
||||||
property string profileImage: ""
|
property string profileImage: ""
|
||||||
property string weatherLocationOverride: "New York, NY"
|
property string weatherLocationOverride: "New York, NY"
|
||||||
|
|
||||||
// Widget visibility preferences for TopBar
|
// Widget visibility preferences for TopBar
|
||||||
property bool showFocusedWindow: true
|
property bool showFocusedWindow: true
|
||||||
property bool showWeather: true
|
property bool showWeather: true
|
||||||
@@ -28,294 +31,284 @@ Singleton {
|
|||||||
property bool showClipboard: true
|
property bool showClipboard: true
|
||||||
property bool showSystemResources: true
|
property bool showSystemResources: true
|
||||||
property bool showSystemTray: true
|
property bool showSystemTray: true
|
||||||
|
|
||||||
// View mode preferences for launchers
|
// View mode preferences for launchers
|
||||||
property string appLauncherViewMode: "list"
|
property string appLauncherViewMode: "list"
|
||||||
property string spotlightLauncherViewMode: "list"
|
property string spotlightLauncherViewMode: "list"
|
||||||
|
|
||||||
// Network preference
|
// Network preference
|
||||||
property string networkPreference: "auto" // "auto", "wifi", "ethernet"
|
property string networkPreference: "auto"
|
||||||
|
|
||||||
|
|
||||||
Component.onCompleted: loadSettings()
|
|
||||||
|
|
||||||
// Monitor system resources preference changes to control service monitoring
|
|
||||||
onShowSystemResourcesChanged: {
|
|
||||||
console.log("Prefs: System resources monitoring", showSystemResources ? "enabled" : "disabled")
|
|
||||||
// Control SystemMonitorService based on whether system monitor widgets are visible
|
|
||||||
if (typeof SystemMonitorService !== 'undefined') {
|
|
||||||
SystemMonitorService.enableTopBarMonitoring(showSystemResources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: settingsFile
|
|
||||||
path: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json"
|
|
||||||
blockLoading: true
|
|
||||||
blockWrites: true
|
|
||||||
watchChanges: true
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
console.log("Settings file loaded successfully")
|
|
||||||
parseSettings(settingsFile.text())
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadFailed: (error) => {
|
|
||||||
console.log("Settings file not found, using defaults. Error:", error)
|
|
||||||
applyStoredTheme()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
parseSettings(settingsFile.text())
|
parseSettings(settingsFile.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSettings(content) {
|
function parseSettings(content) {
|
||||||
try {
|
try {
|
||||||
if (content && content.trim()) {
|
if (content && content.trim()) {
|
||||||
var settings = JSON.parse(content)
|
var settings = JSON.parse(content);
|
||||||
themeIndex = settings.themeIndex !== undefined ? settings.themeIndex : 0
|
themeIndex = settings.themeIndex !== undefined ? settings.themeIndex : 0;
|
||||||
themeIsDynamic = settings.themeIsDynamic !== undefined ? settings.themeIsDynamic : false
|
themeIsDynamic = settings.themeIsDynamic !== undefined ? settings.themeIsDynamic : false;
|
||||||
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false
|
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false;
|
||||||
topBarTransparency = settings.topBarTransparency !== undefined ?
|
topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75;
|
||||||
(settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75
|
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 0.92;
|
||||||
popupTransparency = settings.popupTransparency !== undefined ?
|
recentlyUsedApps = settings.recentlyUsedApps || [];
|
||||||
(settings.popupTransparency > 1 ? settings.popupTransparency / 100.0 : settings.popupTransparency) : 0.92
|
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||||
recentlyUsedApps = settings.recentlyUsedApps || []
|
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
||||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
|
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
||||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
|
profileImage = settings.profileImage !== undefined ? settings.profileImage : "";
|
||||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
weatherLocationOverride = settings.weatherLocationOverride !== undefined ? settings.weatherLocationOverride : "New York, NY";
|
||||||
profileImage = settings.profileImage !== undefined ? settings.profileImage : ""
|
showFocusedWindow = settings.showFocusedWindow !== undefined ? settings.showFocusedWindow : true;
|
||||||
weatherLocationOverride = settings.weatherLocationOverride !== undefined ? settings.weatherLocationOverride : "New York, NY"
|
showWeather = settings.showWeather !== undefined ? settings.showWeather : true;
|
||||||
showFocusedWindow = settings.showFocusedWindow !== undefined ? settings.showFocusedWindow : true
|
showMusic = settings.showMusic !== undefined ? settings.showMusic : true;
|
||||||
showWeather = settings.showWeather !== undefined ? settings.showWeather : true
|
showClipboard = settings.showClipboard !== undefined ? settings.showClipboard : true;
|
||||||
showMusic = settings.showMusic !== undefined ? settings.showMusic : true
|
showSystemResources = settings.showSystemResources !== undefined ? settings.showSystemResources : true;
|
||||||
showClipboard = settings.showClipboard !== undefined ? settings.showClipboard : true
|
showSystemTray = settings.showSystemTray !== undefined ? settings.showSystemTray : true;
|
||||||
showSystemResources = settings.showSystemResources !== undefined ? settings.showSystemResources : true
|
appLauncherViewMode = settings.appLauncherViewMode !== undefined ? settings.appLauncherViewMode : "list";
|
||||||
showSystemTray = settings.showSystemTray !== undefined ? settings.showSystemTray : true
|
spotlightLauncherViewMode = settings.spotlightLauncherViewMode !== undefined ? settings.spotlightLauncherViewMode : "list";
|
||||||
appLauncherViewMode = settings.appLauncherViewMode !== undefined ? settings.appLauncherViewMode : "list"
|
networkPreference = settings.networkPreference !== undefined ? settings.networkPreference : "auto";
|
||||||
spotlightLauncherViewMode = settings.spotlightLauncherViewMode !== undefined ? settings.spotlightLauncherViewMode : "list"
|
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length);
|
||||||
networkPreference = settings.networkPreference !== undefined ? settings.networkPreference : "auto"
|
applyStoredTheme();
|
||||||
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
|
|
||||||
|
|
||||||
applyStoredTheme()
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Settings file is empty - applying default theme")
|
console.log("Settings file is empty - applying default theme");
|
||||||
applyStoredTheme()
|
applyStoredTheme();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Could not parse settings, using defaults:", e)
|
console.log("Could not parse settings, using defaults:", e);
|
||||||
applyStoredTheme()
|
applyStoredTheme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
settingsFile.setText(JSON.stringify({
|
settingsFile.setText(JSON.stringify({
|
||||||
themeIndex,
|
"themeIndex": themeIndex,
|
||||||
themeIsDynamic,
|
"themeIsDynamic": themeIsDynamic,
|
||||||
isLightMode,
|
"isLightMode": isLightMode,
|
||||||
topBarTransparency,
|
"topBarTransparency": topBarTransparency,
|
||||||
popupTransparency,
|
"popupTransparency": popupTransparency,
|
||||||
recentlyUsedApps,
|
"recentlyUsedApps": recentlyUsedApps,
|
||||||
use24HourClock,
|
"use24HourClock": use24HourClock,
|
||||||
useFahrenheit,
|
"useFahrenheit": useFahrenheit,
|
||||||
nightModeEnabled,
|
"nightModeEnabled": nightModeEnabled,
|
||||||
profileImage,
|
"profileImage": profileImage,
|
||||||
weatherLocationOverride,
|
"weatherLocationOverride": weatherLocationOverride,
|
||||||
showFocusedWindow,
|
"showFocusedWindow": showFocusedWindow,
|
||||||
showWeather,
|
"showWeather": showWeather,
|
||||||
showMusic,
|
"showMusic": showMusic,
|
||||||
showClipboard,
|
"showClipboard": showClipboard,
|
||||||
showSystemResources,
|
"showSystemResources": showSystemResources,
|
||||||
showSystemTray,
|
"showSystemTray": showSystemTray,
|
||||||
appLauncherViewMode,
|
"appLauncherViewMode": appLauncherViewMode,
|
||||||
spotlightLauncherViewMode,
|
"spotlightLauncherViewMode": spotlightLauncherViewMode,
|
||||||
networkPreference
|
"networkPreference": networkPreference
|
||||||
}, null, 2))
|
}, null, 2));
|
||||||
console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
|
console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyStoredTheme() {
|
function applyStoredTheme() {
|
||||||
console.log("Applying stored theme:", themeIndex, themeIsDynamic, "lightMode:", isLightMode)
|
console.log("Applying stored theme:", themeIndex, themeIsDynamic, "lightMode:", isLightMode);
|
||||||
|
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
Theme.isLightMode = isLightMode
|
Theme.isLightMode = isLightMode;
|
||||||
Theme.switchTheme(themeIndex, themeIsDynamic, false)
|
Theme.switchTheme(themeIndex, themeIsDynamic, false);
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
Theme.isLightMode = isLightMode
|
Theme.isLightMode = isLightMode;
|
||||||
Theme.switchTheme(themeIndex, themeIsDynamic, false)
|
Theme.switchTheme(themeIndex, themeIsDynamic, false);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTheme(index, isDynamic) {
|
function setTheme(index, isDynamic) {
|
||||||
console.log("Prefs setTheme called - themeIndex:", index, "isDynamic:", isDynamic)
|
console.log("Prefs setTheme called - themeIndex:", index, "isDynamic:", isDynamic);
|
||||||
themeIndex = index
|
themeIndex = index;
|
||||||
themeIsDynamic = isDynamic
|
themeIsDynamic = isDynamic;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLightMode(lightMode) {
|
function setLightMode(lightMode) {
|
||||||
console.log("Prefs setLightMode called - isLightMode:", lightMode)
|
console.log("Prefs setLightMode called - isLightMode:", lightMode);
|
||||||
isLightMode = lightMode
|
isLightMode = lightMode;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTopBarTransparency(transparency) {
|
function setTopBarTransparency(transparency) {
|
||||||
console.log("Prefs setTopBarTransparency called - topBarTransparency:", transparency)
|
console.log("Prefs setTopBarTransparency called - topBarTransparency:", transparency);
|
||||||
topBarTransparency = transparency
|
topBarTransparency = transparency;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPopupTransparency(transparency) {
|
function setPopupTransparency(transparency) {
|
||||||
console.log("Prefs setPopupTransparency called - popupTransparency:", transparency)
|
console.log("Prefs setPopupTransparency called - popupTransparency:", transparency);
|
||||||
popupTransparency = transparency
|
popupTransparency = transparency;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRecentApp(app) {
|
function addRecentApp(app) {
|
||||||
if (!app) return
|
if (!app)
|
||||||
|
return ;
|
||||||
|
|
||||||
var execProp = app.execString || app.exec || ""
|
var execProp = app.execString || app.exec || "";
|
||||||
if (!execProp) return
|
if (!execProp)
|
||||||
|
return ;
|
||||||
|
|
||||||
var existingIndex = -1
|
var existingIndex = -1;
|
||||||
for (var i = 0; i < recentlyUsedApps.length; i++) {
|
for (var i = 0; i < recentlyUsedApps.length; i++) {
|
||||||
if (recentlyUsedApps[i].exec === execProp) {
|
if (recentlyUsedApps[i].exec === execProp) {
|
||||||
existingIndex = i
|
existingIndex = i;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0) {
|
||||||
// App exists, increment usage count
|
// App exists, increment usage count
|
||||||
recentlyUsedApps[existingIndex].usageCount = (recentlyUsedApps[existingIndex].usageCount || 1) + 1
|
recentlyUsedApps[existingIndex].usageCount = (recentlyUsedApps[existingIndex].usageCount || 1) + 1;
|
||||||
recentlyUsedApps[existingIndex].lastUsed = Date.now()
|
recentlyUsedApps[existingIndex].lastUsed = Date.now();
|
||||||
} else {
|
} else {
|
||||||
// New app, create entry
|
// New app, create entry
|
||||||
var appData = {
|
var appData = {
|
||||||
name: app.name || "",
|
"name": app.name || "",
|
||||||
exec: execProp,
|
"exec": execProp,
|
||||||
icon: app.icon || "application-x-executable",
|
"icon": app.icon || "application-x-executable",
|
||||||
comment: app.comment || "",
|
"comment": app.comment || "",
|
||||||
usageCount: 1,
|
"usageCount": 1,
|
||||||
lastUsed: Date.now()
|
"lastUsed": Date.now()
|
||||||
}
|
};
|
||||||
recentlyUsedApps.push(appData)
|
recentlyUsedApps.push(appData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by usage count (descending), then alphabetically by name
|
// Sort by usage count (descending), then alphabetically by name
|
||||||
var sortedApps = recentlyUsedApps.sort(function(a, b) {
|
var sortedApps = recentlyUsedApps.sort(function(a, b) {
|
||||||
if (a.usageCount !== b.usageCount) {
|
if (a.usageCount !== b.usageCount)
|
||||||
return b.usageCount - a.usageCount // Higher usage count first
|
return b.usageCount - a.usageCount;
|
||||||
}
|
|
||||||
return a.name.localeCompare(b.name) // Alphabetical tiebreaker
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Higher usage count first
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
// Limit to 10 apps
|
// Limit to 10 apps
|
||||||
if (sortedApps.length > 10) {
|
if (sortedApps.length > 10)
|
||||||
sortedApps = sortedApps.slice(0, 10)
|
sortedApps = sortedApps.slice(0, 10);
|
||||||
}
|
|
||||||
|
|
||||||
// Reassign to trigger property change signal
|
// Reassign to trigger property change signal
|
||||||
recentlyUsedApps = sortedApps
|
recentlyUsedApps = sortedApps;
|
||||||
|
saveSettings();
|
||||||
saveSettings()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecentApps() {
|
function getRecentApps() {
|
||||||
return recentlyUsedApps
|
return recentlyUsedApps;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New preference setters
|
// New preference setters
|
||||||
function setClockFormat(use24Hour) {
|
function setClockFormat(use24Hour) {
|
||||||
console.log("Prefs setClockFormat called - use24HourClock:", use24Hour)
|
console.log("Prefs setClockFormat called - use24HourClock:", use24Hour);
|
||||||
use24HourClock = use24Hour
|
use24HourClock = use24Hour;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTemperatureUnit(fahrenheit) {
|
function setTemperatureUnit(fahrenheit) {
|
||||||
console.log("Prefs setTemperatureUnit called - useFahrenheit:", fahrenheit)
|
console.log("Prefs setTemperatureUnit called - useFahrenheit:", fahrenheit);
|
||||||
useFahrenheit = fahrenheit
|
useFahrenheit = fahrenheit;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNightModeEnabled(enabled) {
|
function setNightModeEnabled(enabled) {
|
||||||
console.log("Prefs setNightModeEnabled called - nightModeEnabled:", enabled)
|
console.log("Prefs setNightModeEnabled called - nightModeEnabled:", enabled);
|
||||||
nightModeEnabled = enabled
|
nightModeEnabled = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setProfileImage(imageUrl) {
|
function setProfileImage(imageUrl) {
|
||||||
console.log("Prefs setProfileImage called - profileImage:", imageUrl)
|
console.log("Prefs setProfileImage called - profileImage:", imageUrl);
|
||||||
profileImage = imageUrl
|
profileImage = imageUrl;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget visibility setters
|
// Widget visibility setters
|
||||||
function setShowFocusedWindow(enabled) {
|
function setShowFocusedWindow(enabled) {
|
||||||
console.log("Prefs setShowFocusedWindow called - showFocusedWindow:", enabled)
|
console.log("Prefs setShowFocusedWindow called - showFocusedWindow:", enabled);
|
||||||
showFocusedWindow = enabled
|
showFocusedWindow = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShowWeather(enabled) {
|
function setShowWeather(enabled) {
|
||||||
console.log("Prefs setShowWeather called - showWeather:", enabled)
|
console.log("Prefs setShowWeather called - showWeather:", enabled);
|
||||||
showWeather = enabled
|
showWeather = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShowMusic(enabled) {
|
function setShowMusic(enabled) {
|
||||||
console.log("Prefs setShowMusic called - showMusic:", enabled)
|
console.log("Prefs setShowMusic called - showMusic:", enabled);
|
||||||
showMusic = enabled
|
showMusic = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShowClipboard(enabled) {
|
function setShowClipboard(enabled) {
|
||||||
console.log("Prefs setShowClipboard called - showClipboard:", enabled)
|
console.log("Prefs setShowClipboard called - showClipboard:", enabled);
|
||||||
showClipboard = enabled
|
showClipboard = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShowSystemResources(enabled) {
|
function setShowSystemResources(enabled) {
|
||||||
console.log("Prefs setShowSystemResources called - showSystemResources:", enabled)
|
console.log("Prefs setShowSystemResources called - showSystemResources:", enabled);
|
||||||
showSystemResources = enabled
|
showSystemResources = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShowSystemTray(enabled) {
|
function setShowSystemTray(enabled) {
|
||||||
console.log("Prefs setShowSystemTray called - showSystemTray:", enabled)
|
console.log("Prefs setShowSystemTray called - showSystemTray:", enabled);
|
||||||
showSystemTray = enabled
|
showSystemTray = enabled;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// View mode setters
|
// View mode setters
|
||||||
function setAppLauncherViewMode(mode) {
|
function setAppLauncherViewMode(mode) {
|
||||||
console.log("Prefs setAppLauncherViewMode called - appLauncherViewMode:", mode)
|
console.log("Prefs setAppLauncherViewMode called - appLauncherViewMode:", mode);
|
||||||
appLauncherViewMode = mode
|
appLauncherViewMode = mode;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSpotlightLauncherViewMode(mode) {
|
function setSpotlightLauncherViewMode(mode) {
|
||||||
console.log("Prefs setSpotlightLauncherViewMode called - spotlightLauncherViewMode:", mode)
|
console.log("Prefs setSpotlightLauncherViewMode called - spotlightLauncherViewMode:", mode);
|
||||||
spotlightLauncherViewMode = mode
|
spotlightLauncherViewMode = mode;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather location override setter
|
// Weather location override setter
|
||||||
function setWeatherLocationOverride(location) {
|
function setWeatherLocationOverride(location) {
|
||||||
console.log("Prefs setWeatherLocationOverride called - weatherLocationOverride:", location)
|
console.log("Prefs setWeatherLocationOverride called - weatherLocationOverride:", location);
|
||||||
weatherLocationOverride = location
|
weatherLocationOverride = location;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network preference setter
|
// Network preference setter
|
||||||
function setNetworkPreference(preference) {
|
function setNetworkPreference(preference) {
|
||||||
console.log("Prefs setNetworkPreference called - networkPreference:", preference)
|
console.log("Prefs setNetworkPreference called - networkPreference:", preference);
|
||||||
networkPreference = preference
|
networkPreference = preference;
|
||||||
saveSettings()
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: loadSettings()
|
||||||
|
// Monitor system resources preference changes to control service monitoring
|
||||||
|
onShowSystemResourcesChanged: {
|
||||||
|
console.log("Prefs: System resources monitoring", showSystemResources ? "enabled" : "disabled");
|
||||||
|
// Control SystemMonitorService based on whether system monitor widgets are visible
|
||||||
|
if (typeof SystemMonitorService !== 'undefined')
|
||||||
|
SystemMonitorService.enableTopBarMonitoring(showSystemResources);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: settingsFile
|
||||||
|
|
||||||
|
path: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json"
|
||||||
|
blockLoading: true
|
||||||
|
blockWrites: true
|
||||||
|
watchChanges: true
|
||||||
|
onLoaded: {
|
||||||
|
console.log("Settings file loaded successfully");
|
||||||
|
parseSettings(settingsFile.text());
|
||||||
|
}
|
||||||
|
onLoadFailed: (error) => {
|
||||||
|
console.log("Settings file not found, using defaults. Error:", error);
|
||||||
|
applyStoredTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.Pipewire
|
import Quickshell.Services.Pipewire
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Bluetooth
|
import Quickshell.Bluetooth
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,21 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property list<MprisPlayer> availablePlayers: Mpris.players.values
|
readonly property list<MprisPlayer> availablePlayers: Mpris.players.values
|
||||||
|
|
||||||
property MprisPlayer activePlayer: null
|
property MprisPlayer activePlayer: availablePlayers.find(p => p.isPlaying)
|
||||||
property MprisPlayer _candidatePlayer: availablePlayers.find(p => p.isPlaying)
|
|
||||||
?? availablePlayers.find(p => p.canControl && p.canPlay)
|
?? availablePlayers.find(p => p.canControl && p.canPlay)
|
||||||
?? null
|
?? null
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: playerSwitchTimer
|
|
||||||
interval: 300
|
|
||||||
onTriggered: {
|
|
||||||
if (_candidatePlayer !== activePlayer) {
|
|
||||||
activePlayer = _candidatePlayer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on_CandidatePlayerChanged: {
|
|
||||||
if (_candidatePlayer === null && activePlayer !== null) {
|
|
||||||
playerSwitchTimer.restart()
|
|
||||||
} else if (_candidatePlayer !== null) {
|
|
||||||
playerSwitchTimer.stop()
|
|
||||||
activePlayer = _candidatePlayer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "mpris"
|
target: "mpris"
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
import
|
|
||||||
QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Singleton {
|
|||||||
property var wifiNetworks: []
|
property var wifiNetworks: []
|
||||||
property var savedWifiNetworks: []
|
property var savedWifiNetworks: []
|
||||||
property bool isScanning: false
|
property bool isScanning: false
|
||||||
property string connectionStatus: "" // "connecting", "connected", "failed", ""
|
property string connectionStatus: "" // "cosnnecting", "connected", "failed", ""
|
||||||
property string connectingSSID: ""
|
property string connectingSSID: ""
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,42 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Services.UPower
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import Quickshell.Services.UPower
|
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool batteryPopupVisible: false
|
property bool batteryPopupVisible: false
|
||||||
|
|
||||||
visible: batteryPopupVisible
|
function isActiveProfile(profile) {
|
||||||
|
if (typeof PowerProfiles === "undefined")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return PowerProfiles.profile === profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProfile(profile) {
|
||||||
|
if (typeof PowerProfiles === "undefined") {
|
||||||
|
errorToast.show();
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
PowerProfiles.profile = profile;
|
||||||
|
if (PowerProfiles.profile !== profile)
|
||||||
|
errorToast.show();
|
||||||
|
else
|
||||||
|
console.log("Set power profile to: " + PowerProfile.toString(profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: batteryPopupVisible
|
||||||
implicitWidth: 400
|
implicitWidth: 400
|
||||||
implicitHeight: 300
|
implicitHeight: 300
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -34,7 +50,7 @@ PanelWindow {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
batteryPopupVisible = false
|
batteryPopupVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,29 +63,15 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: batteryPopupVisible ? 1 : 0
|
||||||
opacity: batteryPopupVisible ? 1.0 : 0.0
|
scale: batteryPopupVisible ? 1 : 0.85
|
||||||
scale: batteryPopupVisible ? 1.0 : 0.85
|
|
||||||
|
|
||||||
// Prevent click-through to background
|
// Prevent click-through to background
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Consume the click to prevent it from reaching the background
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// Consume the click to prevent it from reaching the background
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +96,10 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: parent.width - 200; height: 1 }
|
Item {
|
||||||
|
width: parent.width - 200
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 32
|
width: 32
|
||||||
@@ -112,14 +117,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closeBatteryArea
|
id: closeBatteryArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
batteryPopupVisible = false
|
batteryPopupVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -142,9 +150,13 @@ PanelWindow {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSizeLarge
|
font.pixelSize: Theme.iconSizeLarge
|
||||||
color: {
|
color: {
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) return Theme.error
|
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||||
if (BatteryService.isCharging) return Theme.primary
|
return Theme.error;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (BatteryService.isCharging)
|
||||||
|
return Theme.primary;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
@@ -160,9 +172,13 @@ PanelWindow {
|
|||||||
text: BatteryService.batteryLevel + "%"
|
text: BatteryService.batteryLevel + "%"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: {
|
color: {
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) return Theme.error
|
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||||
if (BatteryService.isCharging) return Theme.primary
|
return Theme.error;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (BatteryService.isCharging)
|
||||||
|
return Theme.primary;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
@@ -171,29 +187,37 @@ PanelWindow {
|
|||||||
text: BatteryService.batteryStatus
|
text: BatteryService.batteryStatus
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) return Theme.error
|
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||||
if (BatteryService.isCharging) return Theme.primary
|
return Theme.error;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (BatteryService.isCharging)
|
||||||
|
return Theme.primary;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
let time = BatteryService.formatTimeRemaining()
|
let time = BatteryService.formatTimeRemaining();
|
||||||
if (time !== "Unknown") {
|
if (time !== "Unknown")
|
||||||
return BatteryService.isCharging ? "Time until full: " + time : "Time remaining: " + time
|
return BatteryService.isCharging ? "Time until full: " + time : "Time remaining: " + time;
|
||||||
}
|
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No battery info card
|
// No battery info card
|
||||||
@@ -234,8 +258,11 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Battery details
|
// Battery details
|
||||||
@@ -271,11 +298,14 @@ PanelWindow {
|
|||||||
text: BatteryService.batteryHealth
|
text: BatteryService.batteryHealth
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (BatteryService.batteryHealth === "N/A") return Theme.surfaceText
|
if (BatteryService.batteryHealth === "N/A")
|
||||||
var healthNum = parseInt(BatteryService.batteryHealth)
|
return Theme.surfaceText;
|
||||||
return healthNum < 80 ? Theme.error : Theme.surfaceText
|
|
||||||
|
var healthNum = parseInt(BatteryService.batteryHealth);
|
||||||
|
return healthNum < 80 ? Theme.error : Theme.surfaceText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capacity
|
// Capacity
|
||||||
@@ -295,8 +325,11 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power profiles
|
// Power profiles
|
||||||
@@ -317,16 +350,13 @@ PanelWindow {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: (typeof PowerProfiles !== "undefined") ?
|
model: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
||||||
[PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) :
|
|
||||||
[PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
color: profileArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (batteryControlPopup.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
||||||
(batteryControlPopup.isActiveProfile(modelData) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
|
||||||
border.color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : "transparent"
|
border.color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : "transparent"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
@@ -360,22 +390,28 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: profileArea
|
id: profileArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
batteryControlPopup.setProfile(modelData)
|
batteryControlPopup.setProfile(modelData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degradation reason warning
|
// Degradation reason warning
|
||||||
@@ -418,16 +454,44 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
|
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error toast
|
// Error toast
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: errorToast
|
id: errorToast
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
visible = true;
|
||||||
|
hideTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
width: Math.min(300, parent.width - Theme.spacingL * 2)
|
width: Math.min(300, parent.width - Theme.spacingL * 2)
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -448,34 +512,11 @@ PanelWindow {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: hideTimer
|
id: hideTimer
|
||||||
|
|
||||||
interval: 3000
|
interval: 3000
|
||||||
onTriggered: errorToast.visible = false
|
onTriggered: errorToast.visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
|
||||||
visible = true
|
|
||||||
hideTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isActiveProfile(profile) {
|
|
||||||
if (typeof PowerProfiles === "undefined") return false
|
|
||||||
return PowerProfiles.profile === profile
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProfile(profile) {
|
|
||||||
if (typeof PowerProfiles === "undefined") {
|
|
||||||
errorToast.show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerProfiles.profile = profile
|
|
||||||
|
|
||||||
if (PowerProfiles.profile !== profile) {
|
|
||||||
errorToast.show()
|
|
||||||
} else {
|
|
||||||
console.log("Set power profile to: " + PowerProfile.toString(profile))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Services.UPower
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import Quickshell.Services.UPower
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: batteryWidget
|
id: batteryWidget
|
||||||
@@ -13,9 +13,7 @@ Rectangle {
|
|||||||
width: BatteryService.batteryAvailable ? 70 : 40
|
width: BatteryService.batteryAvailable ? 70 : 40
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: batteryArea.containsMouse || batteryPopupVisible ?
|
color: batteryArea.containsMouse || batteryPopupVisible ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
|
||||||
visible: true
|
visible: true
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -27,19 +25,37 @@ Rectangle {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 6
|
font.pixelSize: Theme.iconSize - 6
|
||||||
color: {
|
color: {
|
||||||
if (!BatteryService.batteryAvailable) return Theme.surfaceText
|
if (!BatteryService.batteryAvailable)
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) return Theme.error
|
return Theme.surfaceText;
|
||||||
if (BatteryService.isCharging) return Theme.primary
|
|
||||||
return Theme.surfaceText
|
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
if (BatteryService.isCharging)
|
||||||
|
return Theme.primary;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
SequentialAnimation on opacity {
|
SequentialAnimation on opacity {
|
||||||
running: BatteryService.isCharging
|
running: BatteryService.isCharging
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
NumberAnimation { to: 0.6; duration: 1000; easing.type: Easing.InOutQuad }
|
|
||||||
NumberAnimation { to: 1.0; duration: 1000; easing.type: Easing.InOutQuad }
|
NumberAnimation {
|
||||||
|
to: 0.6
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
to: 1
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -47,30 +63,38 @@ Rectangle {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: {
|
color: {
|
||||||
if (!BatteryService.batteryAvailable) return Theme.surfaceText
|
if (!BatteryService.batteryAvailable)
|
||||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) return Theme.error
|
return Theme.surfaceText;
|
||||||
if (BatteryService.isCharging) return Theme.primary
|
|
||||||
return Theme.surfaceText
|
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
if (BatteryService.isCharging)
|
||||||
|
return Theme.primary;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: BatteryService.batteryAvailable
|
visible: BatteryService.batteryAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: batteryArea
|
id: batteryArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
toggleBatteryPopup()
|
toggleBatteryPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip on hover
|
// Tooltip on hover
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: batteryTooltip
|
id: batteryTooltip
|
||||||
|
|
||||||
width: Math.max(120, tooltipText.contentWidth + Theme.spacingM * 2)
|
width: Math.max(120, tooltipText.contentWidth + Theme.spacingM * 2)
|
||||||
height: tooltipText.contentHeight + Theme.spacingS * 2
|
height: tooltipText.contentHeight + Theme.spacingS * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -78,19 +102,10 @@ Rectangle {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
visible: batteryArea.containsMouse && !batteryPopupVisible
|
visible: batteryArea.containsMouse && !batteryPopupVisible
|
||||||
|
|
||||||
anchors.bottom: parent.top
|
anchors.bottom: parent.top
|
||||||
anchors.bottomMargin: Theme.spacingS
|
anchors.bottomMargin: Theme.spacingS
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
opacity: batteryArea.containsMouse ? 1 : 0
|
||||||
opacity: batteryArea.containsMouse ? 1.0 : 0.0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -98,31 +113,44 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: tooltipText
|
id: tooltipText
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
if (!BatteryService.batteryAvailable) {
|
if (!BatteryService.batteryAvailable) {
|
||||||
if (typeof PowerProfiles === "undefined") return "Power Management"
|
if (typeof PowerProfiles === "undefined")
|
||||||
switch(PowerProfiles.profile) {
|
return "Power Management";
|
||||||
case PowerProfile.PowerSaver: return "Power Profile: Power Saver"
|
|
||||||
case PowerProfile.Performance: return "Power Profile: Performance"
|
switch (PowerProfiles.profile) {
|
||||||
default: return "Power Profile: Balanced"
|
case PowerProfile.PowerSaver:
|
||||||
|
return "Power Profile: Power Saver";
|
||||||
|
case PowerProfile.Performance:
|
||||||
|
return "Power Profile: Performance";
|
||||||
|
default:
|
||||||
|
return "Power Profile: Balanced";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let status = BatteryService.batteryStatus;
|
||||||
let status = BatteryService.batteryStatus
|
let level = BatteryService.batteryLevel + "%";
|
||||||
let level = BatteryService.batteryLevel + "%"
|
let time = BatteryService.formatTimeRemaining();
|
||||||
let time = BatteryService.formatTimeRemaining()
|
if (time !== "Unknown")
|
||||||
|
return status + " • " + level + " • " + time;
|
||||||
if (time !== "Unknown") {
|
else
|
||||||
return status + " • " + level + " • " + time
|
return status + " • " + level;
|
||||||
} else {
|
|
||||||
return status + " • " + level
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -130,5 +158,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,46 +10,44 @@ Column {
|
|||||||
property date displayDate: new Date()
|
property date displayDate: new Date()
|
||||||
property date selectedDate: new Date()
|
property date selectedDate: new Date()
|
||||||
|
|
||||||
spacing: Theme.spacingM
|
function loadEventsForMonth() {
|
||||||
|
if (!CalendarService || !CalendarService.khalAvailable)
|
||||||
|
return ;
|
||||||
|
|
||||||
|
// Calculate date range with padding
|
||||||
|
let firstDay = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
||||||
|
let dayOfWeek = firstDay.getDay();
|
||||||
|
let startDate = new Date(firstDay);
|
||||||
|
startDate.setDate(startDate.getDate() - dayOfWeek - 7); // Extra week padding
|
||||||
|
let lastDay = new Date(displayDate.getFullYear(), displayDate.getMonth() + 1, 0);
|
||||||
|
let endDate = new Date(lastDay);
|
||||||
|
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7); // Extra week padding
|
||||||
|
CalendarService.loadEvents(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: Theme.spacingM
|
||||||
// Load events when display date changes
|
// Load events when display date changes
|
||||||
onDisplayDateChanged: {
|
onDisplayDateChanged: {
|
||||||
loadEventsForMonth()
|
loadEventsForMonth();
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("CalendarWidget: Component completed, CalendarService available:", !!CalendarService);
|
||||||
|
if (CalendarService)
|
||||||
|
console.log("CalendarWidget: khal available:", CalendarService.khalAvailable);
|
||||||
|
|
||||||
|
loadEventsForMonth();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load events when calendar service becomes available
|
// Load events when calendar service becomes available
|
||||||
Connections {
|
Connections {
|
||||||
|
function onKhalAvailableChanged() {
|
||||||
|
if (CalendarService && CalendarService.khalAvailable)
|
||||||
|
loadEventsForMonth();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
target: CalendarService
|
target: CalendarService
|
||||||
enabled: CalendarService !== null
|
enabled: CalendarService !== null
|
||||||
function onKhalAvailableChanged() {
|
|
||||||
if (CalendarService && CalendarService.khalAvailable) {
|
|
||||||
loadEventsForMonth()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
console.log("CalendarWidget: Component completed, CalendarService available:", !!CalendarService)
|
|
||||||
if (CalendarService) {
|
|
||||||
console.log("CalendarWidget: khal available:", CalendarService.khalAvailable)
|
|
||||||
}
|
|
||||||
loadEventsForMonth()
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadEventsForMonth() {
|
|
||||||
if (!CalendarService || !CalendarService.khalAvailable) return
|
|
||||||
|
|
||||||
// Calculate date range with padding
|
|
||||||
let firstDay = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1)
|
|
||||||
let dayOfWeek = firstDay.getDay()
|
|
||||||
let startDate = new Date(firstDay)
|
|
||||||
startDate.setDate(startDate.getDate() - dayOfWeek - 7) // Extra week padding
|
|
||||||
|
|
||||||
let lastDay = new Date(displayDate.getFullYear(), displayDate.getMonth() + 1, 0)
|
|
||||||
let endDate = new Date(lastDay)
|
|
||||||
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7) // Extra week padding
|
|
||||||
|
|
||||||
CalendarService.loadEvents(startDate, endDate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Month navigation header
|
// Month navigation header
|
||||||
@@ -74,16 +72,17 @@ Column {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: prevMonthArea
|
id: prevMonthArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let newDate = new Date(displayDate)
|
let newDate = new Date(displayDate);
|
||||||
newDate.setMonth(newDate.getMonth() - 1)
|
newDate.setMonth(newDate.getMonth() - 1);
|
||||||
displayDate = newDate
|
displayDate = newDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -114,17 +113,19 @@ Column {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: nextMonthArea
|
id: nextMonthArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let newDate = new Date(displayDate)
|
let newDate = new Date(displayDate);
|
||||||
newDate.setMonth(newDate.getMonth() + 1)
|
newDate.setMonth(newDate.getMonth() + 1);
|
||||||
displayDate = newDate
|
displayDate = newDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Days of week header
|
// Days of week header
|
||||||
@@ -147,61 +148,59 @@ Column {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calendar grid
|
// Calendar grid
|
||||||
Grid {
|
Grid {
|
||||||
|
property date firstDay: {
|
||||||
|
let date = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1);
|
||||||
|
let dayOfWeek = date.getDay();
|
||||||
|
date.setDate(date.getDate() - dayOfWeek);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 200 // Fixed height for calendar
|
height: 200 // Fixed height for calendar
|
||||||
columns: 7
|
columns: 7
|
||||||
rows: 6
|
rows: 6
|
||||||
|
|
||||||
property date firstDay: {
|
|
||||||
let date = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1)
|
|
||||||
let dayOfWeek = date.getDay()
|
|
||||||
date.setDate(date.getDate() - dayOfWeek)
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 42
|
model: 42
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width / 7
|
|
||||||
height: parent.height / 6
|
|
||||||
|
|
||||||
property date dayDate: {
|
property date dayDate: {
|
||||||
let date = new Date(parent.firstDay)
|
let date = new Date(parent.firstDay);
|
||||||
date.setDate(date.getDate() + index)
|
date.setDate(date.getDate() + index);
|
||||||
return date
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool isCurrentMonth: dayDate.getMonth() === displayDate.getMonth()
|
property bool isCurrentMonth: dayDate.getMonth() === displayDate.getMonth()
|
||||||
property bool isToday: dayDate.toDateString() === new Date().toDateString()
|
property bool isToday: dayDate.toDateString() === new Date().toDateString()
|
||||||
property bool isSelected: dayDate.toDateString() === selectedDate.toDateString()
|
property bool isSelected: dayDate.toDateString() === selectedDate.toDateString()
|
||||||
|
|
||||||
color: isSelected ? Theme.primary :
|
width: parent.width / 7
|
||||||
isToday ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
height: parent.height / 6
|
||||||
dayArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
color: isSelected ? Theme.primary : isToday ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : dayArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
|
||||||
radius: Theme.cornerRadiusSmall
|
radius: Theme.cornerRadiusSmall
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: dayDate.getDate()
|
text: dayDate.getDate()
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: isSelected ? Theme.surface :
|
color: isSelected ? Theme.surface : isToday ? Theme.primary : isCurrentMonth ? Theme.surfaceText : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
|
||||||
isToday ? Theme.primary :
|
|
||||||
isCurrentMonth ? Theme.surfaceText :
|
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
|
|
||||||
font.weight: isToday || isSelected ? Font.Medium : Font.Normal
|
font.weight: isToday || isSelected ? Font.Medium : Font.Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event indicator - full-width elegant bar
|
// Event indicator - full-width elegant bar
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
// Use a lighter tint of primary for selected state
|
||||||
|
|
||||||
id: eventIndicator
|
id: eventIndicator
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -209,37 +208,32 @@ Column {
|
|||||||
height: 3
|
height: 3
|
||||||
radius: 1.5
|
radius: 1.5
|
||||||
visible: CalendarService && CalendarService.khalAvailable && CalendarService.hasEventsForDate(dayDate)
|
visible: CalendarService && CalendarService.khalAvailable && CalendarService.hasEventsForDate(dayDate)
|
||||||
|
|
||||||
// Dynamic color based on state with opacity
|
// Dynamic color based on state with opacity
|
||||||
color: {
|
color: {
|
||||||
if (isSelected) {
|
if (isSelected)
|
||||||
// Use a lighter tint of primary for selected state
|
return Qt.lighter(Theme.primary, 1.3);
|
||||||
return Qt.lighter(Theme.primary, 1.3)
|
else if (isToday)
|
||||||
} else if (isToday) {
|
return Theme.primary;
|
||||||
return Theme.primary
|
else
|
||||||
} else {
|
return Theme.primary;
|
||||||
return Theme.primary
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: {
|
opacity: {
|
||||||
if (isSelected) {
|
if (isSelected)
|
||||||
return 0.9
|
return 0.9;
|
||||||
} else if (isToday) {
|
else if (isToday)
|
||||||
return 0.8
|
return 0.8;
|
||||||
} else {
|
else
|
||||||
return 0.6
|
return 0.6;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtle animation on hover
|
// Subtle animation on hover
|
||||||
scale: dayArea.containsMouse ? 1.05 : 1.0
|
scale: dayArea.containsMouse ? 1.05 : 1
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -247,6 +241,7 @@ Column {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
@@ -254,20 +249,26 @@ Column {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dayArea
|
id: dayArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
selectedDate = dayDate
|
selectedDate = dayDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -15,14 +15,11 @@ PanelWindow {
|
|||||||
property bool calendarVisible: false
|
property bool calendarVisible: false
|
||||||
|
|
||||||
visible: calendarVisible
|
visible: calendarVisible
|
||||||
|
|
||||||
implicitWidth: 480
|
implicitWidth: 480
|
||||||
implicitHeight: 600
|
implicitHeight: 600
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -34,54 +31,45 @@ PanelWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: mainContainer
|
id: mainContainer
|
||||||
|
|
||||||
|
function calculateWidth() {
|
||||||
|
let baseWidth = 320;
|
||||||
|
if (leftWidgets.hasAnyWidgets)
|
||||||
|
return Math.min(parent.width * 0.9, 600);
|
||||||
|
|
||||||
|
return Math.min(parent.width * 0.7, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateHeight() {
|
||||||
|
let contentHeight = Theme.spacingM * 2; // margins
|
||||||
|
// Main row with widgets and calendar
|
||||||
|
let widgetHeight = 160;
|
||||||
|
// Media widget always present
|
||||||
|
widgetHeight += 140 + Theme.spacingM;
|
||||||
|
// Weather widget always present
|
||||||
|
let calendarHeight = 300;
|
||||||
|
let mainRowHeight = Math.max(widgetHeight, calendarHeight);
|
||||||
|
contentHeight += mainRowHeight + Theme.spacingM;
|
||||||
|
// Add events widget height - use calculated height instead of actual
|
||||||
|
if (CalendarService && CalendarService.khalAvailable) {
|
||||||
|
let hasEvents = eventsWidget.selectedDateEvents && eventsWidget.selectedDateEvents.length > 0;
|
||||||
|
let eventsHeight = hasEvents ? Math.min(300, 80 + eventsWidget.selectedDateEvents.length * 60) : 120;
|
||||||
|
contentHeight += eventsHeight;
|
||||||
|
}
|
||||||
|
return Math.min(contentHeight, parent.height * 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
width: calculateWidth()
|
width: calculateWidth()
|
||||||
height: calculateHeight()
|
height: calculateHeight()
|
||||||
x: (parent.width - width) / 2
|
x: (parent.width - width) / 2
|
||||||
y: Theme.barHeight + 4
|
y: Theme.barHeight + 4
|
||||||
|
|
||||||
function calculateWidth() {
|
|
||||||
let baseWidth = 320
|
|
||||||
if (leftWidgets.hasAnyWidgets) {
|
|
||||||
return Math.min(parent.width * 0.9, 600)
|
|
||||||
}
|
|
||||||
return Math.min(parent.width * 0.7, 400)
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateHeight() {
|
|
||||||
let contentHeight = Theme.spacingM * 2 // margins
|
|
||||||
|
|
||||||
// Main row with widgets and calendar
|
|
||||||
let widgetHeight = 160 // Media widget always present
|
|
||||||
widgetHeight += 140 + Theme.spacingM // Weather widget always present
|
|
||||||
let calendarHeight = 300
|
|
||||||
let mainRowHeight = Math.max(widgetHeight, calendarHeight)
|
|
||||||
|
|
||||||
contentHeight += mainRowHeight + Theme.spacingM
|
|
||||||
|
|
||||||
// Add events widget height - use calculated height instead of actual
|
|
||||||
if (CalendarService && CalendarService.khalAvailable) {
|
|
||||||
let hasEvents = eventsWidget.selectedDateEvents && eventsWidget.selectedDateEvents.length > 0
|
|
||||||
let eventsHeight = hasEvents ? Math.min(300, 80 + eventsWidget.selectedDateEvents.length * 60) : 120
|
|
||||||
contentHeight += eventsHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.min(contentHeight, parent.height * 0.9)
|
|
||||||
}
|
|
||||||
|
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
opacity: calendarVisible ? 1 : 0
|
||||||
shadowEnabled: true
|
scale: calendarVisible ? 1 : 0.92
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 4
|
|
||||||
shadowBlur: 0.5
|
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.15)
|
|
||||||
shadowOpacity: 0.15
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -91,62 +79,45 @@ PanelWindow {
|
|||||||
SequentialAnimation on opacity {
|
SequentialAnimation on opacity {
|
||||||
running: calendarVisible
|
running: calendarVisible
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
to: 0.08
|
to: 0.08
|
||||||
duration: Theme.extraLongDuration
|
duration: Theme.extraLongDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
to: 0.02
|
to: 0.02
|
||||||
duration: Theme.extraLongDuration
|
duration: Theme.extraLongDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: calendarVisible ? 1.0 : 0.0
|
}
|
||||||
scale: calendarVisible ? 1.0 : 0.92
|
|
||||||
|
}
|
||||||
|
|
||||||
// Update height when calendar service events change
|
// Update height when calendar service events change
|
||||||
Connections {
|
Connections {
|
||||||
|
function onEventsByDateChanged() {
|
||||||
|
mainContainer.height = mainContainer.calculateHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKhalAvailableChanged() {
|
||||||
|
mainContainer.height = mainContainer.calculateHeight();
|
||||||
|
}
|
||||||
|
|
||||||
target: CalendarService
|
target: CalendarService
|
||||||
enabled: CalendarService !== null
|
enabled: CalendarService !== null
|
||||||
function onEventsByDateChanged() {
|
|
||||||
mainContainer.height = mainContainer.calculateHeight()
|
|
||||||
}
|
|
||||||
function onKhalAvailableChanged() {
|
|
||||||
mainContainer.height = mainContainer.calculateHeight()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update height when events widget's selectedDateEvents changes
|
// Update height when events widget's selectedDateEvents changes
|
||||||
Connections {
|
Connections {
|
||||||
|
function onSelectedDateEventsChanged() {
|
||||||
|
mainContainer.height = mainContainer.calculateHeight();
|
||||||
|
}
|
||||||
|
|
||||||
target: eventsWidget
|
target: eventsWidget
|
||||||
enabled: eventsWidget !== null
|
enabled: eventsWidget !== null
|
||||||
function onSelectedDateEventsChanged() {
|
|
||||||
mainContainer.height = mainContainer.calculateHeight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.longDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.longDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -158,60 +129,100 @@ PanelWindow {
|
|||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: {
|
height: {
|
||||||
let widgetHeight = 160 // Media widget always present
|
let widgetHeight = 160; // Media widget always present
|
||||||
widgetHeight += 140 + Theme.spacingM // Weather widget always present
|
widgetHeight += 140 + Theme.spacingM; // Weather widget always present
|
||||||
let calendarHeight = 300
|
let calendarHeight = 300;
|
||||||
return Math.max(widgetHeight, calendarHeight)
|
return Math.max(widgetHeight, calendarHeight);
|
||||||
}
|
}
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Left section for widgets
|
// Left section for widgets
|
||||||
Column {
|
Column {
|
||||||
id: leftWidgets
|
id: leftWidgets
|
||||||
|
|
||||||
|
property bool hasAnyWidgets: true // Always show media widget and weather widget
|
||||||
|
|
||||||
width: hasAnyWidgets ? parent.width * 0.45 : 0
|
width: hasAnyWidgets ? parent.width * 0.45 : 0
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
visible: hasAnyWidgets
|
visible: hasAnyWidgets
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
|
||||||
property bool hasAnyWidgets: true // Always show media widget and weather widget
|
|
||||||
|
|
||||||
MediaPlayerWidget {
|
MediaPlayerWidget {
|
||||||
visible: true // Always visible - shows placeholder when no media
|
visible: true // Always visible - shows placeholder when no media
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 160
|
height: 160
|
||||||
}
|
}
|
||||||
|
|
||||||
WeatherWidget {
|
WeatherWidget {
|
||||||
visible: true // Always visible - shows placeholder when no weather
|
visible: true // Always visible - shows placeholder when no weather
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 140
|
height: 140
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right section for calendar
|
// Right section for calendar
|
||||||
CalendarWidget {
|
CalendarWidget {
|
||||||
id: calendarWidget
|
id: calendarWidget
|
||||||
|
|
||||||
width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingL : parent.width
|
width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingL : parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full-width events widget below
|
// Full-width events widget below
|
||||||
EventsWidget {
|
EventsWidget {
|
||||||
id: eventsWidget
|
id: eventsWidget
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
selectedDate: calendarWidget.selectedDate
|
selectedDate: calendarWidget.selectedDate
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 4
|
||||||
|
shadowBlur: 0.5
|
||||||
|
shadowColor: Qt.rgba(0, 0, 0, 0.15)
|
||||||
|
shadowOpacity: 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.longDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.longDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
onClicked: {
|
onClicked: {
|
||||||
calendarVisible = false
|
calendarVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,13 +11,22 @@ Rectangle {
|
|||||||
property date selectedDate: new Date()
|
property date selectedDate: new Date()
|
||||||
property var selectedDateEvents: []
|
property var selectedDateEvents: []
|
||||||
property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0
|
property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0
|
||||||
|
|
||||||
onSelectedDateEventsChanged: {
|
|
||||||
console.log("EventsWidget: selectedDateEvents changed, count:", selectedDateEvents.length)
|
|
||||||
eventsList.model = selectedDateEvents
|
|
||||||
}
|
|
||||||
property bool shouldShow: CalendarService && CalendarService.khalAvailable
|
property bool shouldShow: CalendarService && CalendarService.khalAvailable
|
||||||
|
|
||||||
|
function updateSelectedDateEvents() {
|
||||||
|
if (CalendarService && CalendarService.khalAvailable) {
|
||||||
|
let events = CalendarService.getEventsForDate(selectedDate);
|
||||||
|
console.log("EventsWidget: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events");
|
||||||
|
selectedDateEvents = events;
|
||||||
|
} else {
|
||||||
|
selectedDateEvents = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedDateEventsChanged: {
|
||||||
|
console.log("EventsWidget: selectedDateEvents changed, count:", selectedDateEvents.length);
|
||||||
|
eventsList.model = selectedDateEvents;
|
||||||
|
}
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0
|
height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
@@ -25,58 +34,33 @@ Rectangle {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
visible: shouldShow
|
visible: shouldShow
|
||||||
|
|
||||||
// Material elevation shadow
|
// Material elevation shadow
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
Component.onCompleted: {
|
||||||
shadowEnabled: true
|
updateSelectedDateEvents();
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 2
|
|
||||||
shadowBlur: 0.25
|
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
|
||||||
shadowOpacity: 0.1
|
|
||||||
}
|
}
|
||||||
|
onSelectedDateChanged: {
|
||||||
Behavior on height {
|
updateSelectedDateEvents();
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update events when selected date or events change
|
// Update events when selected date or events change
|
||||||
Connections {
|
Connections {
|
||||||
|
function onEventsByDateChanged() {
|
||||||
|
updateSelectedDateEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKhalAvailableChanged() {
|
||||||
|
updateSelectedDateEvents();
|
||||||
|
}
|
||||||
|
|
||||||
target: CalendarService
|
target: CalendarService
|
||||||
enabled: CalendarService !== null
|
enabled: CalendarService !== null
|
||||||
function onEventsByDateChanged() {
|
|
||||||
updateSelectedDateEvents()
|
|
||||||
}
|
|
||||||
function onKhalAvailableChanged() {
|
|
||||||
updateSelectedDateEvents()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
updateSelectedDateEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelectedDateChanged: {
|
|
||||||
updateSelectedDateEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSelectedDateEvents() {
|
|
||||||
if (CalendarService && CalendarService.khalAvailable) {
|
|
||||||
let events = CalendarService.getEventsForDate(selectedDate)
|
|
||||||
console.log("EventsWidget: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events")
|
|
||||||
selectedDateEvents = events
|
|
||||||
} else {
|
|
||||||
selectedDateEvents = []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header - always visible when widget is shown
|
// Header - always visible when widget is shown
|
||||||
Row {
|
Row {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -92,15 +76,13 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: hasEvents ?
|
text: hasEvents ? (Qt.formatDate(selectedDate, "MMM d") + " • " + (selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) : Qt.formatDate(selectedDate, "MMM d")
|
||||||
(Qt.formatDate(selectedDate, "MMM d") + " • " +
|
|
||||||
(selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) :
|
|
||||||
Qt.formatDate(selectedDate, "MMM d")
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No events placeholder - centered in entire widget (not just content area)
|
// No events placeholder - centered in entire widget (not just content area)
|
||||||
@@ -124,11 +106,13 @@ Rectangle {
|
|||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events list - positioned below header when there are events
|
// Events list - positioned below header when there are events
|
||||||
ListView {
|
ListView {
|
||||||
id: eventsList
|
id: eventsList
|
||||||
|
|
||||||
anchors.top: headerRow.bottom
|
anchors.top: headerRow.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -136,7 +120,7 @@ Rectangle {
|
|||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
anchors.topMargin: Theme.spacingM
|
anchors.topMargin: Theme.spacingM
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
opacity: hasEvents ? 1.0 : 0.0
|
opacity: hasEvents ? 1 : 0
|
||||||
clip: true
|
clip: true
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
boundsMovement: Flickable.StopAtBounds
|
boundsMovement: Flickable.StopAtBounds
|
||||||
@@ -151,6 +135,7 @@ Rectangle {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
@@ -158,20 +143,18 @@ Rectangle {
|
|||||||
height: eventContent.implicitHeight + Theme.spacingM
|
height: eventContent.implicitHeight + Theme.spacingM
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (modelData.url && eventMouseArea.containsMouse) {
|
if (modelData.url && eventMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
} else if (eventMouseArea.containsMouse) {
|
else if (eventMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06);
|
||||||
}
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.06);
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.06)
|
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
if (modelData.url && eventMouseArea.containsMouse) {
|
if (modelData.url && eventMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
|
||||||
} else if (eventMouseArea.containsMouse) {
|
else if (eventMouseArea.containsMouse)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15);
|
||||||
}
|
return "transparent";
|
||||||
return "transparent"
|
|
||||||
}
|
}
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
@@ -189,6 +172,7 @@ Rectangle {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: eventContent
|
id: eventContent
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -196,96 +180,101 @@ Rectangle {
|
|||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
spacing: 6
|
spacing: 6
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: modelData.title
|
text: modelData.title
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Math.max(timeRow.height, locationRow.height)
|
height: Math.max(timeRow.height, locationRow.height)
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: timeRow
|
id: timeRow
|
||||||
spacing: 4
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Text {
|
spacing: 4
|
||||||
text: "schedule"
|
anchors.left: parent.left
|
||||||
font.family: Theme.iconFont
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: "schedule"
|
||||||
if (modelData.allDay) {
|
font.family: Theme.iconFont
|
||||||
return "All day"
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
} else {
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
let timeFormat = Prefs.use24HourClock ? "H:mm" : "h:mm AP"
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
let startTime = Qt.formatTime(modelData.start, timeFormat)
|
|
||||||
if (modelData.start.toDateString() !== modelData.end.toDateString() ||
|
|
||||||
modelData.start.getTime() !== modelData.end.getTime()) {
|
|
||||||
return startTime + " – " + Qt.formatTime(modelData.end, timeFormat)
|
|
||||||
}
|
|
||||||
return startTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
font.weight: Font.Normal
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: locationRow
|
|
||||||
spacing: 4
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: modelData.location !== ""
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: "location_on"
|
|
||||||
font.family: Theme.iconFont
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: modelData.location
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
maximumLineCount: 1
|
|
||||||
width: Math.min(implicitWidth, 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: {
|
||||||
|
if (modelData.allDay) {
|
||||||
|
return "All day";
|
||||||
|
} else {
|
||||||
|
let timeFormat = Prefs.use24HourClock ? "H:mm" : "h:mm AP";
|
||||||
|
let startTime = Qt.formatTime(modelData.start, timeFormat);
|
||||||
|
if (modelData.start.toDateString() !== modelData.end.toDateString() || modelData.start.getTime() !== modelData.end.getTime())
|
||||||
|
return startTime + " – " + Qt.formatTime(modelData.end, timeFormat);
|
||||||
|
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: locationRow
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: modelData.location !== ""
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "location_on"
|
||||||
|
font.family: Theme.iconFont
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData.location
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
maximumLineCount: 1
|
||||||
|
width: Math.min(implicitWidth, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: eventMouseArea
|
id: eventMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: modelData.url ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: modelData.url ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: modelData.url !== ""
|
enabled: modelData.url !== ""
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.url && modelData.url !== "") {
|
if (modelData.url && modelData.url !== "") {
|
||||||
if (Qt.openUrlExternally(modelData.url) === false) {
|
if (Qt.openUrlExternally(modelData.url) === false)
|
||||||
console.warn("Couldn't open", modelData.url)
|
console.warn("Couldn't open", modelData.url);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,6 +284,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on border.color {
|
Behavior on border.color {
|
||||||
@@ -302,7 +292,28 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 2
|
||||||
|
shadowBlur: 0.25
|
||||||
|
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
||||||
|
shadowOpacity: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,87 +10,76 @@ Rectangle {
|
|||||||
id: mediaPlayerWidget
|
id: mediaPlayerWidget
|
||||||
|
|
||||||
property MprisPlayer activePlayer: MprisController.activePlayer
|
property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
|
|
||||||
property string lastValidTitle: ""
|
property string lastValidTitle: ""
|
||||||
property string lastValidArtist: ""
|
property string lastValidArtist: ""
|
||||||
property string lastValidAlbum: ""
|
property string lastValidAlbum: ""
|
||||||
property string lastValidArtUrl: ""
|
property string lastValidArtUrl: ""
|
||||||
|
property real currentPosition: 0
|
||||||
|
|
||||||
Timer {
|
// Simple progress ratio calculation
|
||||||
id: clearCacheTimer
|
function ratio() {
|
||||||
interval: 2000
|
return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0;
|
||||||
onTriggered: {
|
|
||||||
if (!activePlayer) {
|
|
||||||
lastValidTitle = ""
|
|
||||||
lastValidArtist = ""
|
|
||||||
lastValidAlbum = ""
|
|
||||||
lastValidArtUrl = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onActivePlayerChanged: {
|
onActivePlayerChanged: {
|
||||||
if (!activePlayer) {
|
if (!activePlayer)
|
||||||
clearCacheTimer.restart()
|
clearCacheTimer.restart();
|
||||||
} else {
|
else
|
||||||
clearCacheTimer.stop()
|
clearCacheTimer.stop();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
|
||||||
shadowEnabled: true
|
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 2
|
|
||||||
shadowBlur: 0.5
|
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
|
||||||
shadowOpacity: 0.1
|
|
||||||
}
|
|
||||||
|
|
||||||
property real currentPosition: 0
|
Timer {
|
||||||
|
id: clearCacheTimer
|
||||||
|
|
||||||
// Simple progress ratio calculation
|
interval: 2000
|
||||||
function ratio() {
|
onTriggered: {
|
||||||
return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0
|
if (!activePlayer) {
|
||||||
|
lastValidTitle = "";
|
||||||
|
lastValidArtist = "";
|
||||||
|
lastValidAlbum = "";
|
||||||
|
lastValidArtUrl = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates progress bar every 2 seconds when playing
|
// Updates progress bar every 2 seconds when playing
|
||||||
Timer {
|
Timer {
|
||||||
id: positionTimer
|
id: positionTimer
|
||||||
|
|
||||||
interval: 2000
|
interval: 2000
|
||||||
running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking
|
running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
|
if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking)
|
||||||
currentPosition = activePlayer.position
|
currentPosition = activePlayer.position;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend events
|
// Backend events
|
||||||
Connections {
|
Connections {
|
||||||
target: activePlayer
|
|
||||||
|
|
||||||
function onPositionChanged() {
|
function onPositionChanged() {
|
||||||
if (!progressMouseArea.isSeeking) {
|
if (!progressMouseArea.isSeeking)
|
||||||
currentPosition = activePlayer.position
|
currentPosition = activePlayer.position;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPostTrackChanged() {
|
function onPostTrackChanged() {
|
||||||
currentPosition = activePlayer?.position || 0
|
currentPosition = activePlayer && activePlayer.position || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTrackTitleChanged() {
|
function onTrackTitleChanged() {
|
||||||
currentPosition = activePlayer?.position || 0
|
currentPosition = activePlayer && activePlayer.position || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: activePlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -117,6 +106,7 @@ Rectangle {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active content in a column
|
// Active content in a column
|
||||||
@@ -125,11 +115,11 @@ Rectangle {
|
|||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
|
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
|
||||||
|
|
||||||
// Normal media info when playing
|
// Normal media info when playing
|
||||||
Row {
|
Row {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
// Album Art
|
// Album Art
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -144,12 +134,13 @@ Rectangle {
|
|||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: albumArt
|
id: albumArt
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: activePlayer?.trackArtUrl || lastValidArtUrl || ""
|
source: activePlayer && activePlayer.trackArtUrl || lastValidArtUrl || ""
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
if (activePlayer?.trackArtUrl) {
|
if (activePlayer && activePlayer.trackArtUrl)
|
||||||
lastValidArtUrl = activePlayer.trackArtUrl;
|
lastValidArtUrl = activePlayer.trackArtUrl;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
smooth: true
|
smooth: true
|
||||||
@@ -167,8 +158,11 @@ Rectangle {
|
|||||||
font.pixelSize: 28
|
font.pixelSize: 28
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track Info
|
// Track Info
|
||||||
@@ -178,11 +172,11 @@ Rectangle {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackTitle || lastValidTitle || "Unknown Track"
|
text: activePlayer && activePlayer.trackTitle || lastValidTitle || "Unknown Track"
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (activePlayer?.trackTitle) {
|
if (activePlayer && activePlayer.trackTitle)
|
||||||
lastValidTitle = activePlayer.trackTitle;
|
lastValidTitle = activePlayer.trackTitle;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
@@ -192,11 +186,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackArtist || lastValidArtist || "Unknown Artist"
|
text: activePlayer && activePlayer.trackArtist || lastValidArtist || "Unknown Artist"
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (activePlayer?.trackArtist) {
|
if (activePlayer && activePlayer.trackArtist)
|
||||||
lastValidArtist = activePlayer.trackArtist;
|
lastValidArtist = activePlayer.trackArtist;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
||||||
@@ -205,11 +199,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: activePlayer?.trackAlbum || lastValidAlbum || ""
|
text: activePlayer && activePlayer.trackAlbum || lastValidAlbum || ""
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (activePlayer?.trackAlbum) {
|
if (activePlayer && activePlayer.trackAlbum)
|
||||||
lastValidAlbum = activePlayer.trackAlbum;
|
lastValidAlbum = activePlayer.trackAlbum;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||||
@@ -217,124 +211,133 @@ Rectangle {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress bar
|
// Progress bar
|
||||||
Item {
|
Item {
|
||||||
id: progressBarContainer
|
id: progressBarContainer
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: progressBarBackground
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 6
|
height: 24
|
||||||
radius: 3
|
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
|
||||||
visible: activePlayer !== null
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progressFill
|
id: progressBarBackground
|
||||||
height: parent.height
|
|
||||||
radius: parent.radius
|
|
||||||
color: Theme.primary
|
|
||||||
|
|
||||||
width: parent.width * ratio()
|
width: parent.width
|
||||||
|
height: 6
|
||||||
Behavior on width {
|
radius: 3
|
||||||
NumberAnimation { duration: 100 }
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
}
|
visible: activePlayer !== null
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
visible: activePlayer && activePlayer.length > 0
|
Rectangle {
|
||||||
scale: progressMouseArea.containsMouse || progressMouseArea.pressed ? 1.2 : 1.0
|
id: progressFill
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
radius: parent.radius
|
||||||
|
color: Theme.primary
|
||||||
|
width: parent.width * ratio()
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation { duration: 150 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: progressMouseArea
|
id: progressMouseArea
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: activePlayer && activePlayer.length > 0 && activePlayer.canSeek
|
|
||||||
preventStealing: true
|
|
||||||
|
|
||||||
property bool isSeeking: false
|
property bool isSeeking: false
|
||||||
|
|
||||||
onPressed: function(mouse) {
|
anchors.fill: parent
|
||||||
isSeeking = true
|
hoverEnabled: true
|
||||||
if (activePlayer && activePlayer.length > 0) {
|
cursorShape: Qt.PointingHandCursor
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width))
|
enabled: activePlayer && activePlayer.length > 0 && activePlayer.canSeek
|
||||||
let seekPosition = ratio * activePlayer.length
|
preventStealing: true
|
||||||
activePlayer.position = seekPosition
|
onPressed: function(mouse) {
|
||||||
currentPosition = seekPosition
|
isSeeking = true;
|
||||||
|
if (activePlayer && activePlayer.length > 0) {
|
||||||
|
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width));
|
||||||
|
let seekPosition = ratio * activePlayer.length;
|
||||||
|
activePlayer.position = seekPosition;
|
||||||
|
currentPosition = seekPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
isSeeking = false;
|
||||||
|
}
|
||||||
|
onPositionChanged: function(mouse) {
|
||||||
|
if (pressed && isSeeking && activePlayer && activePlayer.length > 0) {
|
||||||
|
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width));
|
||||||
|
let seekPosition = ratio * activePlayer.length;
|
||||||
|
activePlayer.position = seekPosition;
|
||||||
|
currentPosition = seekPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked: function(mouse) {
|
||||||
|
if (activePlayer && activePlayer.length > 0) {
|
||||||
|
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width));
|
||||||
|
let seekPosition = ratio * activePlayer.length;
|
||||||
|
activePlayer.position = seekPosition;
|
||||||
|
currentPosition = seekPosition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
// Global mouse area for drag tracking
|
||||||
isSeeking = false
|
MouseArea {
|
||||||
}
|
id: progressGlobalMouseArea
|
||||||
|
|
||||||
onPositionChanged: function(mouse) {
|
anchors.fill: parent.parent.parent // Fill the entire media player widget
|
||||||
if (pressed && isSeeking && activePlayer && activePlayer.length > 0) {
|
enabled: progressMouseArea.isSeeking
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width))
|
visible: false
|
||||||
let seekPosition = ratio * activePlayer.length
|
preventStealing: true
|
||||||
activePlayer.position = seekPosition
|
onPositionChanged: function(mouse) {
|
||||||
currentPosition = seekPosition
|
if (progressMouseArea.isSeeking && activePlayer && activePlayer.length > 0) {
|
||||||
|
let globalPos = mapToItem(progressBarBackground, mouse.x, mouse.y);
|
||||||
|
let ratio = Math.max(0, Math.min(1, globalPos.x / progressBarBackground.width));
|
||||||
|
let seekPosition = ratio * activePlayer.length;
|
||||||
|
activePlayer.position = seekPosition;
|
||||||
|
currentPosition = seekPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
progressMouseArea.isSeeking = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: function(mouse) {
|
|
||||||
if (activePlayer && activePlayer.length > 0) {
|
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / progressBarBackground.width))
|
|
||||||
let seekPosition = ratio * activePlayer.length
|
|
||||||
activePlayer.position = seekPosition
|
|
||||||
currentPosition = seekPosition
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global mouse area for drag tracking
|
|
||||||
MouseArea {
|
|
||||||
id: progressGlobalMouseArea
|
|
||||||
anchors.fill: parent.parent.parent // Fill the entire media player widget
|
|
||||||
enabled: progressMouseArea.isSeeking
|
|
||||||
visible: false
|
|
||||||
preventStealing: true
|
|
||||||
|
|
||||||
onPositionChanged: function(mouse) {
|
|
||||||
if (progressMouseArea.isSeeking && activePlayer && activePlayer.length > 0) {
|
|
||||||
let globalPos = mapToItem(progressBarBackground, mouse.x, mouse.y)
|
|
||||||
let ratio = Math.max(0, Math.min(1, globalPos.x / progressBarBackground.width))
|
|
||||||
let seekPosition = ratio * activePlayer.length
|
|
||||||
activePlayer.position = seekPosition
|
|
||||||
currentPosition = seekPosition
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onReleased: {
|
|
||||||
progressMouseArea.isSeeking = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control buttons - always visible
|
// Control buttons - always visible
|
||||||
@@ -348,88 +351,108 @@ Rectangle {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
// Previous button
|
// Previous button
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 28
|
width: 28
|
||||||
height: 28
|
height: 28
|
||||||
radius: 14
|
radius: 14
|
||||||
color: prevBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
color: prevBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "skip_previous"
|
text: "skip_previous"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: prevBtnArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
if (!activePlayer) return
|
|
||||||
|
|
||||||
// >8 s → jump to start, otherwise previous track
|
|
||||||
if (currentPosition > 8 && activePlayer.canSeek) {
|
|
||||||
activePlayer.position = 0
|
|
||||||
currentPosition = 0
|
|
||||||
} else {
|
|
||||||
activePlayer.previous()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: prevBtnArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (!activePlayer)
|
||||||
|
return ;
|
||||||
|
|
||||||
|
// >8 s → jump to start, otherwise previous track
|
||||||
|
if (currentPosition > 8 && activePlayer.canSeek) {
|
||||||
|
activePlayer.position = 0;
|
||||||
|
currentPosition = 0;
|
||||||
|
} else {
|
||||||
|
activePlayer.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Play/Pause button
|
||||||
|
Rectangle {
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
radius: 16
|
||||||
|
color: Theme.primary
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
||||||
|
font.family: Theme.iconFont
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: Theme.background
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: activePlayer && activePlayer.togglePlaying()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next button
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: nextBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "skip_next"
|
||||||
|
font.family: Theme.iconFont
|
||||||
|
font.pixelSize: 16
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: nextBtnArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: activePlayer && activePlayer.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play/Pause button
|
|
||||||
Rectangle {
|
|
||||||
width: 32
|
|
||||||
height: 32
|
|
||||||
radius: 16
|
|
||||||
color: Theme.primary
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
|
||||||
font.family: Theme.iconFont
|
|
||||||
font.pixelSize: 20
|
|
||||||
color: Theme.background
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: activePlayer?.togglePlaying()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next button
|
|
||||||
Rectangle {
|
|
||||||
width: 28
|
|
||||||
height: 28
|
|
||||||
radius: 14
|
|
||||||
color: nextBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "skip_next"
|
|
||||||
font.family: Theme.iconFont
|
|
||||||
font.pixelSize: 16
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: nextBtnArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: activePlayer?.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 2
|
||||||
|
shadowBlur: 0.5
|
||||||
|
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
||||||
|
shadowOpacity: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,23 +7,13 @@ import qs.Services
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: weatherWidget
|
id: weatherWidget
|
||||||
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
|
||||||
shadowEnabled: true
|
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 2
|
|
||||||
shadowBlur: 0.5
|
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
|
||||||
shadowOpacity: 0.1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder when no weather - centered in entire widget
|
// Placeholder when no weather - centered in entire widget
|
||||||
Column {
|
Column {
|
||||||
@@ -45,6 +35,7 @@ Rectangle {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather content when available - original Column structure
|
// Weather content when available - original Column structure
|
||||||
@@ -86,9 +77,14 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: if (WeatherService.weather.available) Prefs.setTemperatureUnit(!Prefs.useFahrenheit)
|
onClicked: {
|
||||||
|
if (WeatherService.weather.available)
|
||||||
|
Prefs.setTemperatureUnit(!Prefs.useFahrenheit);
|
||||||
|
|
||||||
|
}
|
||||||
enabled: WeatherService.weather.available
|
enabled: WeatherService.weather.available
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -97,8 +93,11 @@ Rectangle {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather details grid
|
// Weather details grid
|
||||||
@@ -109,6 +108,7 @@ Rectangle {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "humidity_low"
|
text: "humidity_low"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -116,16 +116,19 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--"
|
text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "air"
|
text: "air"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -133,16 +136,19 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: WeatherService.weather.wind || "--"
|
text: WeatherService.weather.wind || "--"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "wb_twilight"
|
text: "wb_twilight"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -150,16 +156,19 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: WeatherService.weather.sunrise || "--"
|
text: WeatherService.weather.sunrise || "--"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "bedtime"
|
text: "bedtime"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -167,13 +176,27 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: WeatherService.weather.sunset || "--"
|
text: WeatherService.weather.sunset || "--"
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 2
|
||||||
|
shadowBlur: 0.5
|
||||||
|
shadowColor: Qt.rgba(0, 0, 0, 0.1)
|
||||||
|
shadowOpacity: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -10,7 +10,6 @@ Item {
|
|||||||
id: audioTab
|
id: audioTab
|
||||||
|
|
||||||
property int audioSubTab: 0 // 0: Output, 1: Input
|
property int audioSubTab: 0 // 0: Output, 1: Input
|
||||||
|
|
||||||
readonly property real volumeLevel: AudioService.volumeLevel
|
readonly property real volumeLevel: AudioService.volumeLevel
|
||||||
readonly property real micLevel: AudioService.micLevel
|
readonly property real micLevel: AudioService.micLevel
|
||||||
readonly property bool volumeMuted: AudioService.sinkMuted
|
readonly property bool volumeMuted: AudioService.sinkMuted
|
||||||
@@ -50,6 +49,7 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: audioTab.audioSubTab = 0
|
onClicked: audioTab.audioSubTab = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -72,7 +72,9 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: audioTab.audioSubTab = 1
|
onClicked: audioTab.audioSubTab = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output Tab Content
|
// Output Tab Content
|
||||||
@@ -115,16 +117,19 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: AudioService.toggleMute()
|
onClicked: AudioService.toggleMute()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: volumeSliderContainer
|
id: volumeSliderContainer
|
||||||
|
|
||||||
width: parent.width - 80
|
width: parent.width - 80
|
||||||
height: 32
|
height: 32
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: volumeSliderTrack
|
id: volumeSliderTrack
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 8
|
height: 8
|
||||||
radius: 4
|
radius: 4
|
||||||
@@ -133,93 +138,99 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: volumeSliderFill
|
id: volumeSliderFill
|
||||||
|
|
||||||
width: parent.width * (audioTab.volumeLevel / 100)
|
width: parent.width * (audioTab.volumeLevel / 100)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation { duration: 100 }
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draggable handle
|
// Draggable handle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: volumeHandle
|
id: volumeHandle
|
||||||
|
|
||||||
width: 18
|
width: 18
|
||||||
height: 18
|
height: 18
|
||||||
radius: 9
|
radius: 9
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
border.color: Qt.lighter(Theme.primary, 1.3)
|
border.color: Qt.lighter(Theme.primary, 1.3)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
x: Math.max(0, Math.min(parent.width - width, volumeSliderFill.width - width / 2))
|
||||||
x: Math.max(0, Math.min(parent.width - width, volumeSliderFill.width - width/2))
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
scale: volumeMouseArea.containsMouse || volumeMouseArea.pressed ? 1.2 : 1
|
||||||
scale: volumeMouseArea.containsMouse || volumeMouseArea.pressed ? 1.2 : 1.0
|
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
NumberAnimation { duration: 150 }
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: volumeMouseArea
|
id: volumeMouseArea
|
||||||
|
|
||||||
|
property bool isDragging: false
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
property bool isDragging: false
|
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
onPressed: (mouse) => {
|
||||||
isDragging = true
|
isDragging = true;
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
|
||||||
let newVolume = Math.round(ratio * 100)
|
let newVolume = Math.round(ratio * 100);
|
||||||
AudioService.setVolume(newVolume)
|
AudioService.setVolume(newVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
isDragging = false
|
isDragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (pressed && isDragging) {
|
if (pressed && isDragging) {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
|
||||||
let newVolume = Math.round(ratio * 100)
|
let newVolume = Math.round(ratio * 100);
|
||||||
AudioService.setVolume(newVolume)
|
AudioService.setVolume(newVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
|
||||||
let newVolume = Math.round(ratio * 100)
|
let newVolume = Math.round(ratio * 100);
|
||||||
AudioService.setVolume(newVolume)
|
AudioService.setVolume(newVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global mouse area for drag tracking
|
// Global mouse area for drag tracking
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: volumeGlobalMouseArea
|
id: volumeGlobalMouseArea
|
||||||
|
|
||||||
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
|
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
|
||||||
enabled: volumeMouseArea.isDragging
|
enabled: volumeMouseArea.isDragging
|
||||||
visible: false
|
visible: false
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (volumeMouseArea.isDragging) {
|
if (volumeMouseArea.isDragging) {
|
||||||
let globalPos = mapToItem(volumeSliderTrack, mouse.x, mouse.y)
|
let globalPos = mapToItem(volumeSliderTrack, mouse.x, mouse.y);
|
||||||
let ratio = Math.max(0, Math.min(1, globalPos.x / volumeSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, globalPos.x / volumeSliderTrack.width));
|
||||||
let newVolume = Math.round(ratio * 100)
|
let newVolume = Math.round(ratio * 100);
|
||||||
AudioService.setVolume(newVolume)
|
AudioService.setVolume(newVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
volumeMouseArea.isDragging = false
|
volumeMouseArea.isDragging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -229,7 +240,9 @@ Item {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output Devices
|
// Output Devices
|
||||||
@@ -273,7 +286,9 @@ Item {
|
|||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real audio devices
|
// Real audio devices
|
||||||
@@ -284,8 +299,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
||||||
(modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
|
||||||
border.color: modelData.active ? Theme.primary : "transparent"
|
border.color: modelData.active ? Theme.primary : "transparent"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
@@ -297,10 +311,14 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.name.includes("bluez")) return "headset"
|
if (modelData.name.includes("bluez"))
|
||||||
else if (modelData.name.includes("hdmi")) return "tv"
|
return "headset";
|
||||||
else if (modelData.name.includes("usb")) return "headset"
|
else if (modelData.name.includes("hdmi"))
|
||||||
else return "speaker"
|
return "tv";
|
||||||
|
else if (modelData.name.includes("usb"))
|
||||||
|
return "headset";
|
||||||
|
else
|
||||||
|
return "speaker";
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
@@ -321,33 +339,39 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.subtitle && modelData.subtitle !== "") {
|
if (modelData.subtitle && modelData.subtitle !== "")
|
||||||
return modelData.subtitle + (modelData.active ? " • Selected" : "")
|
return modelData.subtitle + (modelData.active ? " • Selected" : "");
|
||||||
} else {
|
else
|
||||||
return modelData.active ? "Selected" : ""
|
return modelData.active ? "Selected" : "";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: deviceArea
|
id: deviceArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
AudioService.setAudioSink(modelData.name)
|
AudioService.setAudioSink(modelData.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input Tab Content
|
// Input Tab Content
|
||||||
@@ -390,16 +414,19 @@ Item {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: AudioService.toggleMicMute()
|
onClicked: AudioService.toggleMicMute()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: micSliderContainer
|
id: micSliderContainer
|
||||||
|
|
||||||
width: parent.width - 80
|
width: parent.width - 80
|
||||||
height: 32
|
height: 32
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: micSliderTrack
|
id: micSliderTrack
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 8
|
height: 8
|
||||||
radius: 4
|
radius: 4
|
||||||
@@ -408,93 +435,99 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: micSliderFill
|
id: micSliderFill
|
||||||
|
|
||||||
width: parent.width * (audioTab.micLevel / 100)
|
width: parent.width * (audioTab.micLevel / 100)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation { duration: 100 }
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draggable handle
|
// Draggable handle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: micHandle
|
id: micHandle
|
||||||
|
|
||||||
width: 18
|
width: 18
|
||||||
height: 18
|
height: 18
|
||||||
radius: 9
|
radius: 9
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
border.color: Qt.lighter(Theme.primary, 1.3)
|
border.color: Qt.lighter(Theme.primary, 1.3)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
x: Math.max(0, Math.min(parent.width - width, micSliderFill.width - width / 2))
|
||||||
x: Math.max(0, Math.min(parent.width - width, micSliderFill.width - width/2))
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
scale: micMouseArea.containsMouse || micMouseArea.pressed ? 1.2 : 1
|
||||||
scale: micMouseArea.containsMouse || micMouseArea.pressed ? 1.2 : 1.0
|
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
NumberAnimation { duration: 150 }
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: micMouseArea
|
id: micMouseArea
|
||||||
|
|
||||||
|
property bool isDragging: false
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
property bool isDragging: false
|
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
onPressed: (mouse) => {
|
||||||
isDragging = true
|
isDragging = true;
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
|
||||||
let newMicLevel = Math.round(ratio * 100)
|
let newMicLevel = Math.round(ratio * 100);
|
||||||
AudioService.setMicLevel(newMicLevel)
|
AudioService.setMicLevel(newMicLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
isDragging = false
|
isDragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (pressed && isDragging) {
|
if (pressed && isDragging) {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
|
||||||
let newMicLevel = Math.round(ratio * 100)
|
let newMicLevel = Math.round(ratio * 100);
|
||||||
AudioService.setMicLevel(newMicLevel)
|
AudioService.setMicLevel(newMicLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
|
||||||
let newMicLevel = Math.round(ratio * 100)
|
let newMicLevel = Math.round(ratio * 100);
|
||||||
AudioService.setMicLevel(newMicLevel)
|
AudioService.setMicLevel(newMicLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global mouse area for drag tracking
|
// Global mouse area for drag tracking
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: micGlobalMouseArea
|
id: micGlobalMouseArea
|
||||||
|
|
||||||
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
|
anchors.fill: parent.parent.parent.parent.parent // Fill the entire control center
|
||||||
enabled: micMouseArea.isDragging
|
enabled: micMouseArea.isDragging
|
||||||
visible: false
|
visible: false
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (micMouseArea.isDragging) {
|
if (micMouseArea.isDragging) {
|
||||||
let globalPos = mapToItem(micSliderTrack, mouse.x, mouse.y)
|
let globalPos = mapToItem(micSliderTrack, mouse.x, mouse.y);
|
||||||
let ratio = Math.max(0, Math.min(1, globalPos.x / micSliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, globalPos.x / micSliderTrack.width));
|
||||||
let newMicLevel = Math.round(ratio * 100)
|
let newMicLevel = Math.round(ratio * 100);
|
||||||
AudioService.setMicLevel(newMicLevel)
|
AudioService.setMicLevel(newMicLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
micMouseArea.isDragging = false
|
micMouseArea.isDragging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -504,6 +537,7 @@ Item {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -549,7 +583,9 @@ Item {
|
|||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real audio input devices
|
// Real audio input devices
|
||||||
@@ -560,8 +596,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
||||||
(modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
|
||||||
border.color: modelData.active ? Theme.primary : "transparent"
|
border.color: modelData.active ? Theme.primary : "transparent"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
@@ -573,9 +608,12 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.name.includes("bluez")) return "headset_mic"
|
if (modelData.name.includes("bluez"))
|
||||||
else if (modelData.name.includes("usb")) return "headset_mic"
|
return "headset_mic";
|
||||||
else return "mic"
|
else if (modelData.name.includes("usb"))
|
||||||
|
return "headset_mic";
|
||||||
|
else
|
||||||
|
return "mic";
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
@@ -596,33 +634,41 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.subtitle && modelData.subtitle !== "") {
|
if (modelData.subtitle && modelData.subtitle !== "")
|
||||||
return modelData.subtitle + (modelData.active ? " • Selected" : "")
|
return modelData.subtitle + (modelData.active ? " • Selected" : "");
|
||||||
} else {
|
else
|
||||||
return modelData.active ? "Selected" : ""
|
return modelData.active ? "Selected" : "";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: sourceArea
|
id: sourceArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
AudioService.setAudioSource(modelData.name)
|
AudioService.setAudioSource(modelData.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Bluetooth
|
import Quickshell.Bluetooth
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ Item {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
@@ -25,8 +24,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (BluetoothService.adapter && BluetoothService.adapter.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12))
|
||||||
(BluetoothService.adapter && BluetoothService.adapter.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12))
|
|
||||||
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : "transparent"
|
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : "transparent"
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
@@ -60,19 +58,22 @@ Item {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: bluetoothToggle
|
id: bluetoothToggle
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
BluetoothService.toggleAdapter()
|
BluetoothService.toggleAdapter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -88,14 +89,15 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: BluetoothService.adapter && BluetoothService.adapter.devices ? BluetoothService.adapter.devices.values.filter(dev => dev && dev.paired && BluetoothService.isValidDevice(dev)) : []
|
model: BluetoothService.adapter && BluetoothService.adapter.devices ? BluetoothService.adapter.devices.values.filter((dev) => {
|
||||||
|
return dev && dev.paired && BluetoothService.isValidDevice(dev);
|
||||||
|
}) : []
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: btDeviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
color: btDeviceArea.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) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
||||||
(modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
|
|
||||||
border.color: modelData.connected ? Theme.primary : "transparent"
|
border.color: modelData.connected ? Theme.primary : "transparent"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
@@ -135,32 +137,32 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (modelData.batteryAvailable && modelData.battery > 0) {
|
if (modelData.batteryAvailable && modelData.battery > 0)
|
||||||
return "• " + Math.round(modelData.battery * 100) + "%"
|
return "• " + Math.round(modelData.battery * 100) + "%";
|
||||||
}
|
|
||||||
var btBattery = BatteryService.bluetoothDevices.find(dev =>
|
var btBattery = BatteryService.bluetoothDevices.find((dev) => {
|
||||||
dev.name === (modelData.name || modelData.deviceName) ||
|
return dev.name === (modelData.name || modelData.deviceName) || dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) || (modelData.name || modelData.deviceName).toLowerCase().includes(dev.name.toLowerCase());
|
||||||
dev.name.toLowerCase().includes((modelData.name || modelData.deviceName).toLowerCase()) ||
|
});
|
||||||
(modelData.name || modelData.deviceName).toLowerCase().includes(dev.name.toLowerCase())
|
return btBattery ? "• " + btBattery.percentage + "%" : "";
|
||||||
)
|
|
||||||
return btBattery ? "• " + btBattery.percentage + "%" : ""
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: btMenuButton
|
id: btMenuButton
|
||||||
|
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: btMenuButtonArea.containsMouse ?
|
color: btMenuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -177,36 +179,43 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: btMenuButtonArea
|
id: btMenuButtonArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
bluetoothContextMenuWindow.deviceData = modelData
|
bluetoothContextMenuWindow.deviceData = modelData;
|
||||||
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height)
|
let localPos = btMenuButtonArea.mapToItem(bluetoothTab, btMenuButtonArea.width / 2, btMenuButtonArea.height);
|
||||||
bluetoothContextMenuWindow.show(localPos.x, localPos.y)
|
bluetoothContextMenuWindow.show(localPos.x, localPos.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation { duration: Theme.shortDuration }
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: btDeviceArea
|
id: btDeviceArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 40
|
anchors.rightMargin: 40
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
BluetoothService.debugDevice(modelData)
|
BluetoothService.debugDevice(modelData);
|
||||||
BluetoothService.toggle(modelData.address)
|
BluetoothService.toggle(modelData.address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -226,7 +235,10 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: 1; height: 1 }
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.max(140, scanText.contentWidth + Theme.spacingL * 2)
|
width: Math.max(140, scanText.contentWidth + Theme.spacingL * 2)
|
||||||
@@ -250,29 +262,32 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: scanText
|
id: scanText
|
||||||
|
|
||||||
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Stop Scanning" : "Start Scanning"
|
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Stop Scanning" : "Start Scanning"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: scanArea
|
id: scanArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (BluetoothService.adapter && BluetoothService.adapter.discovering) {
|
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
||||||
BluetoothService.stopScan()
|
BluetoothService.stopScan();
|
||||||
} else {
|
else
|
||||||
BluetoothService.startScan()
|
BluetoothService.startScan();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
@@ -286,15 +301,25 @@ Item {
|
|||||||
height: 70
|
height: 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (availableDeviceArea.containsMouse) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
if (availableDeviceArea.containsMouse)
|
||||||
if (modelData.pairing) return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||||
if (modelData.blocked) return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08)
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
if (modelData.pairing)
|
||||||
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||||
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08);
|
||||||
|
|
||||||
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
if (modelData.pairing) return Theme.warning
|
if (modelData.pairing)
|
||||||
if (modelData.blocked) return Theme.error
|
return Theme.warning;
|
||||||
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2);
|
||||||
}
|
}
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
@@ -309,9 +334,13 @@ Item {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
color: {
|
color: {
|
||||||
if (modelData.pairing) return Theme.warning
|
if (modelData.pairing)
|
||||||
if (modelData.blocked) return Theme.error
|
return Theme.warning;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
@@ -324,9 +353,13 @@ Item {
|
|||||||
text: modelData.name || modelData.deviceName
|
text: modelData.name || modelData.deviceName
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (modelData.pairing) return Theme.warning
|
if (modelData.pairing)
|
||||||
if (modelData.blocked) return Theme.error
|
return Theme.warning;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: modelData.pairing ? Font.Medium : Font.Normal
|
font.weight: modelData.pairing ? Font.Medium : Font.Normal
|
||||||
}
|
}
|
||||||
@@ -340,16 +373,23 @@ Item {
|
|||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
switch (pairingStatus) {
|
switch (pairingStatus) {
|
||||||
case "pairing": return "Pairing..."
|
case "pairing":
|
||||||
case "blocked": return "Blocked"
|
return "Pairing...";
|
||||||
default: return BluetoothService.getSignalStrength(modelData)
|
case "blocked":
|
||||||
|
return "Blocked";
|
||||||
|
default:
|
||||||
|
return BluetoothService.getSignalStrength(modelData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: {
|
color: {
|
||||||
if (modelData.pairing) return Theme.warning
|
if (modelData.pairing)
|
||||||
if (modelData.blocked) return Theme.error
|
return Theme.warning;
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,9 +407,13 @@ Item {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
visible: modelData.rssi !== undefined && modelData.rssi !== 0 && pairingStatus === "available"
|
visible: modelData.rssi !== undefined && modelData.rssi !== 0 && pairingStatus === "available"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -380,20 +424,28 @@ Item {
|
|||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: {
|
color: {
|
||||||
if (!canPair && !modelData.pairing) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
if (!canPair && !modelData.pairing)
|
||||||
if (actionButtonArea.containsMouse) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3);
|
||||||
return "transparent"
|
|
||||||
|
if (actionButtonArea.containsMouse)
|
||||||
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||||
|
|
||||||
|
return "transparent";
|
||||||
}
|
}
|
||||||
border.color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
border.color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
opacity: canPair || modelData.pairing ? 1.0 : 0.5
|
opacity: canPair || modelData.pairing ? 1 : 0.5
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
if (modelData.pairing) return "Pairing..."
|
if (modelData.pairing)
|
||||||
if (modelData.blocked) return "Blocked"
|
return "Pairing...";
|
||||||
return "Pair"
|
|
||||||
|
if (modelData.blocked)
|
||||||
|
return "Blocked";
|
||||||
|
|
||||||
|
return "Pair";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
color: canPair || modelData.pairing ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
@@ -402,34 +454,37 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: actionButtonArea
|
id: actionButtonArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: canPair
|
enabled: canPair
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (canPair) {
|
if (canPair)
|
||||||
BluetoothService.pair(modelData.address)
|
BluetoothService.pair(modelData.address);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: availableDeviceArea
|
id: availableDeviceArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 90 // Don't overlap with action button
|
anchors.rightMargin: 90 // Don't overlap with action button
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: canPair ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: canPair
|
enabled: canPair
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (canPair) {
|
if (canPair)
|
||||||
BluetoothService.pair(modelData.address)
|
BluetoothService.pair(modelData.address);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -455,6 +510,7 @@ Item {
|
|||||||
to: 360
|
to: 360
|
||||||
duration: 2000
|
duration: 2000
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -464,6 +520,7 @@ Item {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -472,6 +529,7 @@ Item {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -483,15 +541,39 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: bluetoothContextMenuWindow
|
id: bluetoothContextMenuWindow
|
||||||
|
|
||||||
property var deviceData: null
|
property var deviceData: null
|
||||||
property bool menuVisible: false
|
property bool menuVisible: false
|
||||||
|
|
||||||
|
function show(x, y) {
|
||||||
|
const menuWidth = 160;
|
||||||
|
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2;
|
||||||
|
let finalX = x - menuWidth / 2;
|
||||||
|
let finalY = y;
|
||||||
|
finalX = Math.max(0, Math.min(finalX, bluetoothTab.width - menuWidth));
|
||||||
|
finalY = Math.max(0, Math.min(finalY, bluetoothTab.height - menuHeight));
|
||||||
|
bluetoothContextMenuWindow.x = finalX;
|
||||||
|
bluetoothContextMenuWindow.y = finalY;
|
||||||
|
bluetoothContextMenuWindow.visible = true;
|
||||||
|
bluetoothContextMenuWindow.menuVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
bluetoothContextMenuWindow.menuVisible = false;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
bluetoothContextMenuWindow.visible = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
width: 160
|
width: 160
|
||||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
@@ -500,6 +582,8 @@ Item {
|
|||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
z: 1000
|
z: 1000
|
||||||
|
opacity: menuVisible ? 1 : 0
|
||||||
|
scale: menuVisible ? 1 : 0.85
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -512,25 +596,9 @@ Item {
|
|||||||
z: parent.z - 1
|
z: parent.z - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: menuVisible ? 1.0 : 0.0
|
|
||||||
scale: menuVisible ? 1.0 : 0.85
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: menuColumn
|
id: menuColumn
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
spacing: 1
|
spacing: 1
|
||||||
@@ -563,19 +631,20 @@ Item {
|
|||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: connectArea
|
id: connectArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (bluetoothContextMenuWindow.deviceData) {
|
if (bluetoothContextMenuWindow.deviceData)
|
||||||
BluetoothService.toggle(bluetoothContextMenuWindow.deviceData.address)
|
BluetoothService.toggle(bluetoothContextMenuWindow.deviceData.address);
|
||||||
}
|
|
||||||
bluetoothContextMenuWindow.hide()
|
bluetoothContextMenuWindow.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +653,9 @@ Item {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -599,6 +670,7 @@ Item {
|
|||||||
height: 1
|
height: 1
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -629,19 +701,20 @@ Item {
|
|||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: forgetArea
|
id: forgetArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (bluetoothContextMenuWindow.deviceData) {
|
if (bluetoothContextMenuWindow.deviceData)
|
||||||
BluetoothService.forget(bluetoothContextMenuWindow.deviceData.address)
|
BluetoothService.forget(bluetoothContextMenuWindow.deviceData.address);
|
||||||
}
|
|
||||||
bluetoothContextMenuWindow.hide()
|
bluetoothContextMenuWindow.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,37 +723,36 @@ Item {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function show(x, y) {
|
Behavior on opacity {
|
||||||
const menuWidth = 160
|
NumberAnimation {
|
||||||
const menuHeight = menuColumn.implicitHeight + Theme.spacingS * 2
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
let finalX = x - menuWidth / 2
|
|
||||||
let finalY = y
|
|
||||||
|
|
||||||
finalX = Math.max(0, Math.min(finalX, bluetoothTab.width - menuWidth))
|
|
||||||
finalY = Math.max(0, Math.min(finalY, bluetoothTab.height - menuHeight))
|
|
||||||
|
|
||||||
bluetoothContextMenuWindow.x = finalX
|
|
||||||
bluetoothContextMenuWindow.y = finalY
|
|
||||||
bluetoothContextMenuWindow.visible = true
|
|
||||||
bluetoothContextMenuWindow.menuVisible = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
Behavior on scale {
|
||||||
bluetoothContextMenuWindow.menuVisible = false
|
NumberAnimation {
|
||||||
Qt.callLater(() => { bluetoothContextMenuWindow.visible = false })
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: bluetoothContextMenuWindow.visible
|
visible: bluetoothContextMenuWindow.visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
bluetoothContextMenuWindow.hide()
|
bluetoothContextMenuWindow.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -691,5 +763,7 @@ Item {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -12,21 +12,19 @@ PanelWindow {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool controlCenterVisible: false
|
property bool controlCenterVisible: false
|
||||||
|
property string currentTab: "network" // "network", "audio", "bluetooth", "display"
|
||||||
|
property bool powerOptionsExpanded: false
|
||||||
|
|
||||||
visible: controlCenterVisible
|
visible: controlCenterVisible
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
// Enable/disable WiFi auto-refresh based on control center visibility
|
// Enable/disable WiFi auto-refresh based on control center visibility
|
||||||
WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled
|
WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: 600
|
implicitWidth: 600
|
||||||
implicitHeight: 500
|
implicitHeight: 500
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -36,9 +34,6 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
property string currentTab: "network" // "network", "audio", "bluetooth", "display"
|
|
||||||
property bool powerOptionsExpanded: false
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.min(600, Screen.width - Theme.spacingL * 2)
|
width: Math.min(600, Screen.width - Theme.spacingL * 2)
|
||||||
height: root.powerOptionsExpanded ? 570 : 500
|
height: root.powerOptionsExpanded ? 570 : 500
|
||||||
@@ -48,50 +43,66 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: controlCenterVisible ? 1 : 0
|
||||||
// TopBar dropdown animation - optimized for performance
|
// TopBar dropdown animation - optimized for performance
|
||||||
transform: [
|
transform: [
|
||||||
Scale {
|
Scale {
|
||||||
id: scaleTransform
|
id: scaleTransform
|
||||||
origin.x: 600 // Use fixed width since popup is max 600px wide
|
|
||||||
|
origin.x: 600 // Use fixed width since popup is max 600px wide
|
||||||
origin.y: 0
|
origin.y: 0
|
||||||
xScale: controlCenterVisible ? 1.0 : 0.95
|
xScale: controlCenterVisible ? 1 : 0.95
|
||||||
yScale: controlCenterVisible ? 1.0 : 0.8
|
yScale: controlCenterVisible ? 1 : 0.8
|
||||||
},
|
},
|
||||||
Translate {
|
Translate {
|
||||||
id: translateTransform
|
id: translateTransform
|
||||||
x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
|
|
||||||
|
x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
|
||||||
y: controlCenterVisible ? 0 : -30
|
y: controlCenterVisible ? 0 : -30
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Single coordinated animation for better performance
|
// Single coordinated animation for better performance
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "visible"
|
name: "visible"
|
||||||
when: controlCenterVisible
|
when: controlCenterVisible
|
||||||
PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 }
|
|
||||||
PropertyChanges { target: translateTransform; x: 0; y: 0 }
|
PropertyChanges {
|
||||||
|
target: scaleTransform
|
||||||
|
xScale: 1
|
||||||
|
yScale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: translateTransform
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "hidden"
|
name: "hidden"
|
||||||
when: !controlCenterVisible
|
when: !controlCenterVisible
|
||||||
PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 }
|
|
||||||
PropertyChanges { target: translateTransform; x: 15; y: -30 }
|
PropertyChanges {
|
||||||
|
target: scaleTransform
|
||||||
|
xScale: 0.95
|
||||||
|
yScale: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: translateTransform
|
||||||
|
x: 15
|
||||||
|
y: -30
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Power menu height animation
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration // Faster for height changes
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
from: "*"; to: "*"
|
from: "*"
|
||||||
|
to: "*"
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
targets: [scaleTransform, translateTransform]
|
targets: [scaleTransform, translateTransform]
|
||||||
@@ -99,19 +110,12 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
opacity: controlCenterVisible ? 1.0 : 0.0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
@@ -140,11 +144,12 @@ PanelWindow {
|
|||||||
// Profile Picture Container
|
// Profile Picture Container
|
||||||
Item {
|
Item {
|
||||||
id: avatarContainer
|
id: avatarContainer
|
||||||
width: 64
|
|
||||||
height: 64
|
|
||||||
|
|
||||||
property bool hasImage: profileImageLoader.status === Image.Ready
|
property bool hasImage: profileImageLoader.status === Image.Ready
|
||||||
|
|
||||||
|
width: 64
|
||||||
|
height: 64
|
||||||
|
|
||||||
// This rectangle provides the themed ring via its border.
|
// This rectangle provides the themed ring via its border.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -158,12 +163,15 @@ PanelWindow {
|
|||||||
// Hidden Image loader. Its only purpose is to load the texture.
|
// Hidden Image loader. Its only purpose is to load the texture.
|
||||||
Image {
|
Image {
|
||||||
id: profileImageLoader
|
id: profileImageLoader
|
||||||
|
|
||||||
source: {
|
source: {
|
||||||
if (Prefs.profileImage === "") return ""
|
if (Prefs.profileImage === "")
|
||||||
if (Prefs.profileImage.startsWith("/")) {
|
return "";
|
||||||
return "file://" + Prefs.profileImage
|
|
||||||
}
|
if (Prefs.profileImage.startsWith("/"))
|
||||||
return Prefs.profileImage
|
return "file://" + Prefs.profileImage;
|
||||||
|
|
||||||
|
return Prefs.profileImage;
|
||||||
}
|
}
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
@@ -180,11 +188,12 @@ PanelWindow {
|
|||||||
maskSource: circularMask
|
maskSource: circularMask
|
||||||
visible: avatarContainer.hasImage
|
visible: avatarContainer.hasImage
|
||||||
maskThresholdMin: 0.5
|
maskThresholdMin: 0.5
|
||||||
maskSpreadAtMin: 1.0
|
maskSpreadAtMin: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: circularMask
|
id: circularMask
|
||||||
|
|
||||||
width: 64 - 10
|
width: 64 - 10
|
||||||
height: 64 - 10
|
height: 64 - 10
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
@@ -197,6 +206,7 @@ PanelWindow {
|
|||||||
color: "black"
|
color: "black"
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for when there is no image.
|
// Fallback for when there is no image.
|
||||||
@@ -213,6 +223,7 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.iconSize + 8
|
font.pixelSize: Theme.iconSize + 8
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error icon for when the image fails to load.
|
// Error icon for when the image fails to load.
|
||||||
@@ -224,6 +235,7 @@ PanelWindow {
|
|||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
visible: Prefs.profileImage !== "" && profileImageLoader.status === Image.Error
|
visible: Prefs.profileImage !== "" && profileImageLoader.status === Image.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// User Info Text
|
// User Info Text
|
||||||
@@ -231,7 +243,6 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: UserInfoService.fullName || UserInfoService.username || "User"
|
text: UserInfoService.fullName || UserInfoService.username || "User"
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
@@ -245,7 +256,9 @@ PanelWindow {
|
|||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action Buttons - Power and Settings
|
// Action Buttons - Power and Settings
|
||||||
@@ -260,9 +273,7 @@ PanelWindow {
|
|||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
radius: 20
|
radius: 20
|
||||||
color: powerButton.containsMouse || root.powerOptionsExpanded ?
|
color: powerButton.containsMouse || root.powerOptionsExpanded ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -285,31 +296,40 @@ PanelWindow {
|
|||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: parent
|
target: parent
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
to: 0.0
|
to: 0
|
||||||
duration: Theme.shortDuration / 2
|
duration: Theme.shortDuration / 2
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
PropertyAction { target: parent; property: "text" }
|
|
||||||
|
PropertyAction {
|
||||||
|
target: parent
|
||||||
|
property: "text"
|
||||||
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: parent
|
target: parent
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
to: 1.0
|
to: 1
|
||||||
duration: Theme.shortDuration / 2
|
duration: Theme.shortDuration / 2
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: powerButton
|
id: powerButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.powerOptionsExpanded = !root.powerOptionsExpanded
|
root.powerOptionsExpanded = !root.powerOptionsExpanded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +338,9 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings Button
|
// Settings Button
|
||||||
@@ -326,9 +348,7 @@ PanelWindow {
|
|||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
radius: 20
|
radius: 20
|
||||||
color: settingsButton.containsMouse ?
|
color: settingsButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -340,13 +360,13 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: settingsButton
|
id: settingsButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
controlCenterVisible = false
|
controlCenterVisible = false;
|
||||||
settingsPopup.settingsVisible = true
|
settingsPopup.settingsVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,9 +375,13 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animated Collapsible Power Options (optimized)
|
// Animated Collapsible Power Options (optimized)
|
||||||
@@ -368,24 +392,9 @@ PanelWindow {
|
|||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4)
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: root.powerOptionsExpanded ? 1 : 0
|
border.width: root.powerOptionsExpanded ? 1 : 0
|
||||||
opacity: root.powerOptionsExpanded ? 1.0 : 0.0
|
opacity: root.powerOptionsExpanded ? 1 : 0
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
// Single coordinated animation for power options
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
@@ -396,9 +405,7 @@ PanelWindow {
|
|||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: logoutButton.containsMouse ?
|
color: logoutButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -419,21 +426,22 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: logoutButton
|
id: logoutButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.powerOptionsExpanded = false
|
root.powerOptionsExpanded = false;
|
||||||
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
||||||
root.powerConfirmDialog.powerConfirmAction = "logout"
|
root.powerConfirmDialog.powerConfirmAction = "logout";
|
||||||
root.powerConfirmDialog.powerConfirmTitle = "Logout"
|
root.powerConfirmDialog.powerConfirmTitle = "Logout";
|
||||||
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?"
|
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?";
|
||||||
root.powerConfirmDialog.powerConfirmVisible = true
|
root.powerConfirmDialog.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,7 +451,9 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reboot
|
// Reboot
|
||||||
@@ -451,9 +461,7 @@ PanelWindow {
|
|||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: rebootButton.containsMouse ?
|
color: rebootButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -474,21 +482,22 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: rebootButton
|
id: rebootButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.powerOptionsExpanded = false
|
root.powerOptionsExpanded = false;
|
||||||
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
||||||
root.powerConfirmDialog.powerConfirmAction = "reboot"
|
root.powerConfirmDialog.powerConfirmAction = "reboot";
|
||||||
root.powerConfirmDialog.powerConfirmTitle = "Restart"
|
root.powerConfirmDialog.powerConfirmTitle = "Restart";
|
||||||
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?"
|
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?";
|
||||||
root.powerConfirmDialog.powerConfirmVisible = true
|
root.powerConfirmDialog.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,7 +507,9 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown
|
// Shutdown
|
||||||
@@ -506,9 +517,7 @@ PanelWindow {
|
|||||||
width: 100
|
width: 100
|
||||||
height: 34
|
height: 34
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: shutdownButton.containsMouse ?
|
color: shutdownButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -529,21 +538,22 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: shutdownButton
|
id: shutdownButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.powerOptionsExpanded = false
|
root.powerOptionsExpanded = false;
|
||||||
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
if (typeof root !== "undefined" && root.powerConfirmDialog) {
|
||||||
root.powerConfirmDialog.powerConfirmAction = "poweroff"
|
root.powerConfirmDialog.powerConfirmAction = "poweroff";
|
||||||
root.powerConfirmDialog.powerConfirmTitle = "Shutdown"
|
root.powerConfirmDialog.powerConfirmTitle = "Shutdown";
|
||||||
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to shutdown?"
|
root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to shutdown?";
|
||||||
root.powerConfirmDialog.powerConfirmVisible = true
|
root.powerConfirmDialog.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,9 +563,30 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single coordinated animation for power options
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab buttons
|
// Tab buttons
|
||||||
@@ -565,36 +596,51 @@ PanelWindow {
|
|||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: {
|
model: {
|
||||||
let tabs = [
|
let tabs = [{
|
||||||
{name: "Network", icon: "wifi", id: "network", available: true}
|
"name": "Network",
|
||||||
]
|
"icon": "wifi",
|
||||||
|
"id": "network",
|
||||||
|
"available": true
|
||||||
|
}];
|
||||||
// Always show audio
|
// Always show audio
|
||||||
tabs.push({name: "Audio", icon: "volume_up", id: "audio", available: true})
|
tabs.push({
|
||||||
|
"name": "Audio",
|
||||||
|
"icon": "volume_up",
|
||||||
|
"id": "audio",
|
||||||
|
"available": true
|
||||||
|
});
|
||||||
// Show Bluetooth only if available
|
// Show Bluetooth only if available
|
||||||
if (BluetoothService.available) {
|
if (BluetoothService.available)
|
||||||
tabs.push({name: "Bluetooth", icon: "bluetooth", id: "bluetooth", available: true})
|
tabs.push({
|
||||||
}
|
"name": "Bluetooth",
|
||||||
|
"icon": "bluetooth",
|
||||||
|
"id": "bluetooth",
|
||||||
|
"available": true
|
||||||
|
});
|
||||||
|
|
||||||
// Always show display
|
// Always show display
|
||||||
tabs.push({name: "Display", icon: "brightness_6", id: "display", available: true})
|
tabs.push({
|
||||||
|
"name": "Display",
|
||||||
return tabs
|
"icon": "brightness_6",
|
||||||
|
"id": "display",
|
||||||
|
"available": true
|
||||||
|
});
|
||||||
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property int tabCount: {
|
property int tabCount: {
|
||||||
let count = 3 // Network + Audio + Display (always visible)
|
let count = 3; // Network + Audio + Display (always visible)
|
||||||
if (BluetoothService.available) count++
|
if (BluetoothService.available)
|
||||||
return count
|
count++;
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
|
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
|
||||||
height: 40
|
height: 40
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: root.currentTab === modelData.id ?
|
color: root.currentTab === modelData.id ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -615,16 +661,17 @@ PanelWindow {
|
|||||||
font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal
|
font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: tabArea
|
id: tabArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.currentTab = modelData.id
|
root.currentTab = modelData.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,10 +680,15 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab content area
|
// Tab content area
|
||||||
@@ -646,19 +698,11 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network Tab
|
// Network Tab
|
||||||
NetworkTab {
|
NetworkTab {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
visible: root.currentTab === "network"
|
visible: root.currentTab === "network"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio Tab
|
// Audio Tab
|
||||||
@@ -680,10 +724,37 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
visible: root.currentTab === "display"
|
visible: root.currentTab === "display"
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Power menu height animation
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration // Faster for height changes
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to close
|
// Click outside to close
|
||||||
@@ -691,7 +762,8 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
onClicked: {
|
onClicked: {
|
||||||
controlCenterVisible = false
|
controlCenterVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: displayTab
|
id: displayTab
|
||||||
clip: true
|
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -35,11 +35,11 @@ ScrollView {
|
|||||||
leftIcon: "brightness_low"
|
leftIcon: "brightness_low"
|
||||||
rightIcon: "brightness_high"
|
rightIcon: "brightness_high"
|
||||||
enabled: BrightnessService.brightnessAvailable
|
enabled: BrightnessService.brightnessAvailable
|
||||||
|
|
||||||
onSliderValueChanged: function(newValue) {
|
onSliderValueChanged: function(newValue) {
|
||||||
BrightnessService.setBrightness(newValue)
|
BrightnessService.setBrightness(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display settings
|
// Display settings
|
||||||
@@ -64,9 +64,7 @@ ScrollView {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Prefs.nightModeEnabled ?
|
color: Prefs.nightModeEnabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (nightModeToggle.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))
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
(nightModeToggle.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))
|
|
||||||
border.color: Prefs.nightModeEnabled ? Theme.primary : "transparent"
|
border.color: Prefs.nightModeEnabled ? Theme.primary : "transparent"
|
||||||
border.width: Prefs.nightModeEnabled ? 1 : 0
|
border.width: Prefs.nightModeEnabled ? 1 : 0
|
||||||
|
|
||||||
@@ -89,26 +87,28 @@ ScrollView {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: nightModeToggle
|
id: nightModeToggle
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (Prefs.nightModeEnabled) {
|
if (Prefs.nightModeEnabled) {
|
||||||
// Disable night mode - kill any running color temperature processes
|
// Disable night mode - kill any running color temperature processes
|
||||||
nightModeDisableProcess.running = true
|
nightModeDisableProcess.running = true;
|
||||||
Prefs.setNightModeEnabled(false)
|
Prefs.setNightModeEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
// Enable night mode using wlsunset or redshift
|
// Enable night mode using wlsunset or redshift
|
||||||
nightModeEnableProcess.running = true
|
nightModeEnableProcess.running = true;
|
||||||
Prefs.setNightModeEnabled(true)
|
Prefs.setNightModeEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Light/Dark mode toggle
|
// Light/Dark mode toggle
|
||||||
@@ -116,9 +116,7 @@ ScrollView {
|
|||||||
width: (parent.width - Theme.spacingM) / 2
|
width: (parent.width - Theme.spacingM) / 2
|
||||||
height: 80
|
height: 80
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.isLightMode ?
|
color: Theme.isLightMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (lightModeToggle.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))
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
(lightModeToggle.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))
|
|
||||||
border.color: Theme.isLightMode ? Theme.primary : "transparent"
|
border.color: Theme.isLightMode ? Theme.primary : "transparent"
|
||||||
border.width: Theme.isLightMode ? 1 : 0
|
border.width: Theme.isLightMode ? 1 : 0
|
||||||
|
|
||||||
@@ -141,16 +139,17 @@ ScrollView {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: lightModeToggle
|
id: lightModeToggle
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Theme.toggleLightMode()
|
Theme.toggleLightMode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,35 +158,41 @@ ScrollView {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Night mode processes
|
// Night mode processes
|
||||||
Process {
|
Process {
|
||||||
id: nightModeEnableProcess
|
id: nightModeEnableProcess
|
||||||
|
|
||||||
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to enable night mode")
|
console.warn("Failed to enable night mode");
|
||||||
Prefs.setNightModeEnabled(false)
|
Prefs.setNightModeEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: nightModeDisableProcess
|
id: nightModeDisableProcess
|
||||||
|
|
||||||
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
|
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0)
|
||||||
console.warn("Failed to disable night mode")
|
console.warn("Failed to disable night mode");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
// Default to WiFi when nothing is connected
|
||||||
|
|
||||||
id: networkTab
|
id: networkTab
|
||||||
|
|
||||||
property int networkSubTab: {
|
property int networkSubTab: {
|
||||||
// Default to WiFi tab if WiFi is connected, otherwise Ethernet
|
// Default to WiFi tab if WiFi is connected, otherwise Ethernet
|
||||||
if (NetworkService.networkStatus === "wifi") return 1
|
if (NetworkService.networkStatus === "wifi")
|
||||||
else if (NetworkService.networkStatus === "ethernet") return 0
|
return 1;
|
||||||
else return 1 // Default to WiFi when nothing is connected
|
else if (NetworkService.networkStatus === "ethernet")
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -30,9 +34,7 @@ Item {
|
|||||||
width: (parent.width - Theme.spacingXS) / 2
|
width: (parent.width - Theme.spacingXS) / 2
|
||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadiusSmall
|
radius: Theme.cornerRadiusSmall
|
||||||
color: networkTab.networkSubTab === 0 ?
|
color: networkTab.networkSubTab === 0 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : ethernetTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
ethernetTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -53,27 +55,28 @@ Item {
|
|||||||
font.weight: networkTab.networkSubTab === 0 ? Font.Medium : Font.Normal
|
font.weight: networkTab.networkSubTab === 0 ? Font.Medium : Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: ethernetTabArea
|
id: ethernetTabArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
networkTab.networkSubTab = 0
|
networkTab.networkSubTab = 0;
|
||||||
WifiService.autoRefreshEnabled = false
|
WifiService.autoRefreshEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: (parent.width - Theme.spacingXS) / 2
|
width: (parent.width - Theme.spacingXS) / 2
|
||||||
height: 36
|
height: 36
|
||||||
radius: Theme.cornerRadiusSmall
|
radius: Theme.cornerRadiusSmall
|
||||||
color: networkTab.networkSubTab === 1 ?
|
color: networkTab.networkSubTab === 1 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : wifiTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
wifiTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -94,22 +97,26 @@ Item {
|
|||||||
font.weight: networkTab.networkSubTab === 1 ? Font.Medium : Font.Normal
|
font.weight: networkTab.networkSubTab === 1 ? Font.Medium : Font.Normal
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: wifiTabArea
|
id: wifiTabArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
networkTab.networkSubTab = 1
|
networkTab.networkSubTab = 1;
|
||||||
WifiService.autoRefreshEnabled = true
|
WifiService.autoRefreshEnabled = true;
|
||||||
if (NetworkService.wifiEnabled) {
|
if (NetworkService.wifiEnabled)
|
||||||
WifiService.scanWifi()
|
WifiService.scanWifi();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethernet Tab Content
|
// Ethernet Tab Content
|
||||||
@@ -124,12 +131,9 @@ Item {
|
|||||||
flickDeceleration: 8000
|
flickDeceleration: 8000
|
||||||
maximumFlickVelocity: 15000
|
maximumFlickVelocity: 15000
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
policy: ScrollBar.AsNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: ethernetContent
|
id: ethernetContent
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
@@ -173,6 +177,7 @@ Item {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force Ethernet preference button
|
// Force Ethernet preference button
|
||||||
@@ -185,22 +190,16 @@ Item {
|
|||||||
radius: 6
|
radius: 6
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
z: 10
|
z: 10
|
||||||
opacity: networkTab.changingNetworkPreference ? 0.6 : 1.0
|
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
|
||||||
visible: networkTab.networkStatus !== "ethernet"
|
visible: networkTab.networkStatus !== "ethernet"
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: ethernetPreferenceIcon
|
id: ethernetPreferenceIcon
|
||||||
|
|
||||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -218,16 +217,17 @@ Item {
|
|||||||
duration: 1000
|
duration: 1000
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.changingNetworkPreference ? "Switching..." :
|
text: networkTab.changingNetworkPreference ? "Switching..." : (networkTab.networkStatus === "ethernet" ? "" : "Prefer over WiFi")
|
||||||
(networkTab.networkStatus === "ethernet" ? "" : "Prefer over WiFi")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: networkTab.networkStatus === "ethernet" ? Theme.background : Theme.primary
|
color: networkTab.networkStatus === "ethernet" ? Theme.background : Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -237,18 +237,29 @@ Item {
|
|||||||
propagateComposedEvents: false
|
propagateComposedEvents: false
|
||||||
enabled: !networkTab.changingNetworkPreference
|
enabled: !networkTab.changingNetworkPreference
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("*** ETHERNET PREFERENCE BUTTON CLICKED ***")
|
console.log("*** ETHERNET PREFERENCE BUTTON CLICKED ***");
|
||||||
if (networkTab.networkStatus !== "ethernet") {
|
if (networkTab.networkStatus !== "ethernet") {
|
||||||
console.log("Setting preference to ethernet")
|
console.log("Setting preference to ethernet");
|
||||||
NetworkService.setNetworkPreference("ethernet")
|
NetworkService.setNetworkPreference("ethernet");
|
||||||
} else {
|
} else {
|
||||||
console.log("Setting preference to auto")
|
console.log("Setting preference to auto");
|
||||||
NetworkService.setNetworkPreference("auto")
|
NetworkService.setNetworkPreference("auto");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethernet control button
|
// Ethernet control button
|
||||||
@@ -279,19 +290,28 @@ Item {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: ethernetControlArea
|
id: ethernetControlArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
NetworkService.toggleNetworkConnection("ethernet")
|
NetworkService.toggleNetworkConnection("ethernet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
policy: ScrollBar.AsNeeded
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi Tab Content
|
// WiFi Tab Content
|
||||||
@@ -306,12 +326,9 @@ Item {
|
|||||||
flickDeceleration: 8000
|
flickDeceleration: 8000
|
||||||
maximumFlickVelocity: 15000
|
maximumFlickVelocity: 15000
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
policy: ScrollBar.AsNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: wifiContent
|
id: wifiContent
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
@@ -322,14 +339,7 @@ Item {
|
|||||||
radius: Theme.cornerRadius
|
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)
|
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
|
visible: NetworkService.wifiAvailable
|
||||||
opacity: NetworkService.wifiToggling ? 0.6 : 1.0
|
opacity: NetworkService.wifiToggling ? 0.6 : 1
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -339,6 +349,7 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: wifiToggleIcon
|
id: wifiToggleIcon
|
||||||
|
|
||||||
text: NetworkService.wifiToggling ? "sync" : "power_settings_new"
|
text: NetworkService.wifiToggling ? "sync" : "power_settings_new"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
@@ -361,7 +372,9 @@ Item {
|
|||||||
duration: 200
|
duration: 200
|
||||||
easing.type: Easing.OutQuad
|
easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -371,17 +384,28 @@ Item {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: wifiToggleArea
|
id: wifiToggleArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
NetworkService.toggleWifiRadio()
|
NetworkService.toggleWifiRadio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current WiFi connection (if connected)
|
// Current WiFi connection (if connected)
|
||||||
@@ -401,11 +425,7 @@ Item {
|
|||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: networkTab.networkStatus === "wifi" ?
|
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"
|
||||||
(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.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSizeLarge
|
font.pixelSize: Theme.iconSizeLarge
|
||||||
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||||
@@ -428,6 +448,7 @@ Item {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force WiFi preference button
|
// Force WiFi preference button
|
||||||
@@ -439,22 +460,16 @@ Item {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
radius: 6
|
radius: 6
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
opacity: networkTab.changingNetworkPreference ? 0.6 : 1.0
|
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
|
||||||
visible: networkTab.networkStatus !== "wifi"
|
visible: networkTab.networkStatus !== "wifi"
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: wifiPreferenceIcon
|
id: wifiPreferenceIcon
|
||||||
|
|
||||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -472,16 +487,17 @@ Item {
|
|||||||
duration: 1000
|
duration: 1000
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: NetworkService.changingNetworkPreference ? "Switching..." :
|
text: NetworkService.changingNetworkPreference ? "Switching..." : (NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
|
||||||
(NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
|
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -491,16 +507,26 @@ Item {
|
|||||||
propagateComposedEvents: false
|
propagateComposedEvents: false
|
||||||
enabled: !networkTab.changingNetworkPreference
|
enabled: !networkTab.changingNetworkPreference
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Force WiFi preference clicked")
|
console.log("Force WiFi preference clicked");
|
||||||
if (NetworkService.networkStatus !== "wifi") {
|
if (NetworkService.networkStatus !== "wifi")
|
||||||
NetworkService.setNetworkPreference("wifi")
|
NetworkService.setNetworkPreference("wifi");
|
||||||
} else {
|
else
|
||||||
NetworkService.setNetworkPreference("auto")
|
NetworkService.setNetworkPreference("auto");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available WiFi Networks
|
// Available WiFi Networks
|
||||||
@@ -519,17 +545,20 @@ Item {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: parent.width - 200; height: 1 }
|
Item {
|
||||||
|
width: parent.width - 200
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
color: refreshArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
color: refreshArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : WifiService.isScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||||
WifiService.isScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: refreshIcon
|
id: refreshIcon
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: WifiService.isScanning ? "sync" : "refresh"
|
text: WifiService.isScanning ? "sync" : "refresh"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -552,24 +581,28 @@ Item {
|
|||||||
duration: 200
|
duration: 200
|
||||||
easing.type: Easing.OutQuad
|
easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: refreshArea
|
id: refreshArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: !WifiService.isScanning
|
enabled: !WifiService.isScanning
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (NetworkService.wifiEnabled) {
|
if (NetworkService.wifiEnabled)
|
||||||
WifiService.scanWifi()
|
WifiService.scanWifi();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Connection status indicator
|
// Connection status indicator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -577,24 +610,22 @@ Item {
|
|||||||
height: 40
|
height: 40
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
if (WifiService.connectionStatus === "connecting") {
|
if (WifiService.connectionStatus === "connecting")
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||||
} else if (WifiService.connectionStatus === "failed") {
|
else if (WifiService.connectionStatus === "failed")
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
||||||
} else if (WifiService.connectionStatus === "connected") {
|
else if (WifiService.connectionStatus === "connected")
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12)
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12);
|
||||||
}
|
return "transparent";
|
||||||
return "transparent"
|
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
if (WifiService.connectionStatus === "connecting") {
|
if (WifiService.connectionStatus === "connecting")
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3)
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
|
||||||
} else if (WifiService.connectionStatus === "failed") {
|
else if (WifiService.connectionStatus === "failed")
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
||||||
} else if (WifiService.connectionStatus === "connected") {
|
else if (WifiService.connectionStatus === "connected")
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3)
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3);
|
||||||
}
|
return "transparent";
|
||||||
return "transparent"
|
|
||||||
}
|
}
|
||||||
border.width: WifiService.connectionStatus !== "" ? 1 : 0
|
border.width: WifiService.connectionStatus !== "" ? 1 : 0
|
||||||
visible: WifiService.connectionStatus !== ""
|
visible: WifiService.connectionStatus !== ""
|
||||||
@@ -605,19 +636,32 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: connectionIcon
|
id: connectionIcon
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
if (WifiService.connectionStatus === "connecting") return "sync"
|
if (WifiService.connectionStatus === "connecting")
|
||||||
if (WifiService.connectionStatus === "failed") return "error"
|
return "sync";
|
||||||
if (WifiService.connectionStatus === "connected") return "check_circle"
|
|
||||||
return ""
|
if (WifiService.connectionStatus === "failed")
|
||||||
|
return "error";
|
||||||
|
|
||||||
|
if (WifiService.connectionStatus === "connected")
|
||||||
|
return "check_circle";
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 6
|
font.pixelSize: Theme.iconSize - 6
|
||||||
color: {
|
color: {
|
||||||
if (WifiService.connectionStatus === "connecting") return Theme.warning
|
if (WifiService.connectionStatus === "connecting")
|
||||||
if (WifiService.connectionStatus === "failed") return Theme.error
|
return Theme.warning;
|
||||||
if (WifiService.connectionStatus === "connected") return Theme.success
|
|
||||||
return Theme.surfaceText
|
if (WifiService.connectionStatus === "failed")
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
if (WifiService.connectionStatus === "connected")
|
||||||
|
return Theme.success;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
rotation: WifiService.connectionStatus === "connecting" ? connectionIcon.rotation : 0
|
rotation: WifiService.connectionStatus === "connecting" ? connectionIcon.rotation : 0
|
||||||
@@ -637,25 +681,40 @@ Item {
|
|||||||
duration: 200
|
duration: 200
|
||||||
easing.type: Easing.OutQuad
|
easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (WifiService.connectionStatus === "connecting") return "Connecting to " + WifiService.connectingSSID
|
if (WifiService.connectionStatus === "connecting")
|
||||||
if (WifiService.connectionStatus === "failed") return "Failed to connect to " + WifiService.connectingSSID
|
return "Connecting to " + WifiService.connectingSSID;
|
||||||
if (WifiService.connectionStatus === "connected") return "Connected to " + WifiService.connectingSSID
|
|
||||||
return ""
|
if (WifiService.connectionStatus === "failed")
|
||||||
|
return "Failed to connect to " + WifiService.connectingSSID;
|
||||||
|
|
||||||
|
if (WifiService.connectionStatus === "connected")
|
||||||
|
return "Connected to " + WifiService.connectingSSID;
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (WifiService.connectionStatus === "connecting") return Theme.warning
|
if (WifiService.connectionStatus === "connecting")
|
||||||
if (WifiService.connectionStatus === "failed") return Theme.error
|
return Theme.warning;
|
||||||
if (WifiService.connectionStatus === "connected") return Theme.success
|
|
||||||
return Theme.surfaceText
|
if (WifiService.connectionStatus === "failed")
|
||||||
|
return Theme.error;
|
||||||
|
|
||||||
|
if (WifiService.connectionStatus === "connected")
|
||||||
|
return Theme.success;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -663,7 +722,9 @@ Item {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi networks list (only show if WiFi is available and enabled)
|
// WiFi networks list (only show if WiFi is available and enabled)
|
||||||
@@ -674,8 +735,7 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
radius: Theme.cornerRadiusSmall
|
radius: Theme.cornerRadiusSmall
|
||||||
color: networkArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
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"
|
||||||
modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
|
||||||
border.color: modelData.connected ? Theme.primary : "transparent"
|
border.color: modelData.connected ? Theme.primary : "transparent"
|
||||||
border.width: modelData.connected ? 1 : 0
|
border.width: modelData.connected ? 1 : 0
|
||||||
|
|
||||||
@@ -686,12 +746,10 @@ Item {
|
|||||||
// Signal strength icon
|
// Signal strength icon
|
||||||
Text {
|
Text {
|
||||||
id: signalIcon
|
id: signalIcon
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: modelData.signalStrength === "excellent" ? "wifi" :
|
text: modelData.signalStrength === "excellent" ? "wifi" : modelData.signalStrength === "good" ? "wifi_2_bar" : modelData.signalStrength === "fair" ? "wifi_1_bar" : modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi"
|
||||||
modelData.signalStrength === "good" ? "wifi_2_bar" :
|
|
||||||
modelData.signalStrength === "fair" ? "wifi_1_bar" :
|
|
||||||
modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi"
|
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize
|
font.pixelSize: Theme.iconSize
|
||||||
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
||||||
@@ -718,19 +776,25 @@ Item {
|
|||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: {
|
text: {
|
||||||
if (modelData.connected) return "Connected"
|
if (modelData.connected)
|
||||||
if (modelData.saved) return "Saved" + (modelData.secured ? " • Secured" : " • Open")
|
return "Connected";
|
||||||
return modelData.secured ? "Secured" : "Open"
|
|
||||||
|
if (modelData.saved)
|
||||||
|
return "Saved" + (modelData.secured ? " • Secured" : " • Open");
|
||||||
|
|
||||||
|
return modelData.secured ? "Secured" : "Open";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right side icons
|
// Right side icons
|
||||||
Row {
|
Row {
|
||||||
id: rightIcons
|
id: rightIcons
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
@@ -763,44 +827,52 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: forgetArea
|
id: forgetArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
WifiService.forgetWifiNetwork(modelData.ssid)
|
WifiService.forgetWifiNetwork(modelData.ssid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Already connected, do nothing or show info
|
||||||
|
|
||||||
id: networkArea
|
id: networkArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (modelData.connected) {
|
if (modelData.connected)
|
||||||
// Already connected, do nothing or show info
|
return ;
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelData.saved) {
|
if (modelData.saved) {
|
||||||
// Saved network, connect directly
|
// Saved network, connect directly
|
||||||
WifiService.connectToWifi(modelData.ssid)
|
WifiService.connectToWifi(modelData.ssid);
|
||||||
} else if (modelData.secured) {
|
} else if (modelData.secured) {
|
||||||
// Secured network, need password - use root dialog
|
// Secured network, need password - use root dialog
|
||||||
wifiPasswordDialog.wifiPasswordSSID = modelData.ssid
|
wifiPasswordDialog.wifiPasswordSSID = modelData.ssid;
|
||||||
wifiPasswordDialog.wifiPasswordInput = ""
|
wifiPasswordDialog.wifiPasswordInput = "";
|
||||||
wifiPasswordDialog.wifiPasswordDialogVisible = true
|
wifiPasswordDialog.wifiPasswordDialogVisible = true;
|
||||||
} else {
|
} else {
|
||||||
// Open network, connect directly
|
// Open network, connect directly
|
||||||
WifiService.connectToWifi(modelData.ssid)
|
WifiService.connectToWifi(modelData.ssid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi disabled message
|
// WiFi disabled message
|
||||||
@@ -831,9 +903,17 @@ Item {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
policy: ScrollBar.AsNeeded
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import "."
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import "."
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: cpuWidget
|
id: cpuWidget
|
||||||
@@ -13,19 +13,17 @@ Rectangle {
|
|||||||
width: 55
|
width: 55
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: cpuArea.containsMouse ?
|
color: cpuArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cpuArea
|
id: cpuArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ProcessMonitorService.setSortBy("cpu")
|
ProcessMonitorService.setSortBy("cpu");
|
||||||
processListDropdown.toggle()
|
processListDropdown.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,14 +33,18 @@ Rectangle {
|
|||||||
|
|
||||||
// CPU icon
|
// CPU icon
|
||||||
Text {
|
Text {
|
||||||
text: "memory" // Material Design memory icon (swapped from RAM widget)
|
text: "memory" // Material Design memory icon (swapped from RAM widget)
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: {
|
color: {
|
||||||
if (SystemMonitorService.cpuUsage > 80) return Theme.error
|
if (SystemMonitorService.cpuUsage > 80)
|
||||||
if (SystemMonitorService.cpuUsage > 60) return Theme.warning
|
return Theme.error;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (SystemMonitorService.cpuUsage > 60)
|
||||||
|
return Theme.warning;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
@@ -55,5 +57,7 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,48 +50,48 @@ Item {
|
|||||||
// Slider track
|
// Slider track
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: sliderTrack
|
id: sliderTrack
|
||||||
width: parent.width - (leftIconWidth + rightIconWidth + (slider.leftIcon.length > 0 ? Theme.spacingM : 0) + (slider.rightIcon.length > 0 ? Theme.spacingM : 0))
|
|
||||||
height: 6
|
|
||||||
radius: 3
|
|
||||||
color: slider.enabled ?
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) :
|
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
property int leftIconWidth: slider.leftIcon.length > 0 ? Theme.iconSize : 0
|
property int leftIconWidth: slider.leftIcon.length > 0 ? Theme.iconSize : 0
|
||||||
property int rightIconWidth: slider.rightIcon.length > 0 ? Theme.iconSize : 0
|
property int rightIconWidth: slider.rightIcon.length > 0 ? Theme.iconSize : 0
|
||||||
|
|
||||||
|
width: parent.width - (leftIconWidth + rightIconWidth + (slider.leftIcon.length > 0 ? Theme.spacingM : 0) + (slider.rightIcon.length > 0 ? Theme.spacingM : 0))
|
||||||
|
height: 6
|
||||||
|
radius: 3
|
||||||
|
color: slider.enabled ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// Fill
|
// Fill
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: sliderFill
|
id: sliderFill
|
||||||
|
|
||||||
width: parent.width * ((slider.value - slider.minimum) / (slider.maximum - slider.minimum))
|
width: parent.width * ((slider.value - slider.minimum) / (slider.maximum - slider.minimum))
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText
|
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draggable handle
|
// Draggable handle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: sliderHandle
|
id: sliderHandle
|
||||||
|
|
||||||
width: 18
|
width: 18
|
||||||
height: 18
|
height: 18
|
||||||
radius: 9
|
radius: 9
|
||||||
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText
|
color: slider.enabled ? Theme.primary : Theme.surfaceVariantText
|
||||||
border.color: slider.enabled ? Qt.lighter(Theme.primary, 1.3) : Qt.lighter(Theme.surfaceVariantText, 1.3)
|
border.color: slider.enabled ? Qt.lighter(Theme.primary, 1.3) : Qt.lighter(Theme.surfaceVariantText, 1.3)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
x: Math.max(0, Math.min(parent.width - width, sliderFill.width - width / 2))
|
||||||
x: Math.max(0, Math.min(parent.width - width, sliderFill.width - width/2))
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
scale: sliderMouseArea.containsMouse || sliderMouseArea.pressed ? 1.2 : 1
|
||||||
scale: sliderMouseArea.containsMouse || sliderMouseArea.pressed ? 1.2 : 1.0
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation { duration: 150 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle glow effect when active
|
// Handle glow effect when active
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -105,57 +105,67 @@ Item {
|
|||||||
visible: sliderMouseArea.containsMouse && slider.enabled
|
visible: sliderMouseArea.containsMouse && slider.enabled
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 150 }
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: sliderContainer
|
id: sliderContainer
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: sliderMouseArea
|
id: sliderMouseArea
|
||||||
|
|
||||||
|
property bool isDragging: false
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: slider.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: slider.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: slider.enabled
|
enabled: slider.enabled
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
property bool isDragging: false
|
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
onPressed: (mouse) => {
|
||||||
if (slider.enabled) {
|
if (slider.enabled) {
|
||||||
isDragging = true
|
isDragging = true;
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum))
|
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||||
slider.value = newValue
|
slider.value = newValue;
|
||||||
slider.sliderValueChanged(newValue)
|
slider.sliderValueChanged(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (slider.enabled) {
|
if (slider.enabled) {
|
||||||
isDragging = false
|
isDragging = false;
|
||||||
slider.sliderDragFinished(slider.value)
|
slider.sliderDragFinished(slider.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (pressed && isDragging && slider.enabled) {
|
if (pressed && isDragging && slider.enabled) {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum))
|
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||||
slider.value = newValue
|
slider.value = newValue;
|
||||||
slider.sliderValueChanged(newValue)
|
slider.sliderValueChanged(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
if (slider.enabled) {
|
if (slider.enabled) {
|
||||||
let ratio = Math.max(0, Math.min(1, mouse.x / width))
|
let ratio = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum))
|
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||||
slider.value = newValue
|
slider.value = newValue;
|
||||||
slider.sliderValueChanged(newValue)
|
slider.sliderValueChanged(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,29 +173,30 @@ Item {
|
|||||||
// Global mouse area for drag tracking
|
// Global mouse area for drag tracking
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: sliderGlobalMouseArea
|
id: sliderGlobalMouseArea
|
||||||
|
|
||||||
anchors.fill: sliderContainer
|
anchors.fill: sliderContainer
|
||||||
enabled: sliderMouseArea.isDragging
|
enabled: sliderMouseArea.isDragging
|
||||||
visible: false
|
visible: false
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
|
|
||||||
onPositionChanged: (mouse) => {
|
onPositionChanged: (mouse) => {
|
||||||
if (sliderMouseArea.isDragging && slider.enabled) {
|
if (sliderMouseArea.isDragging && slider.enabled) {
|
||||||
let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y)
|
let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y);
|
||||||
let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width))
|
let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width));
|
||||||
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum))
|
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
|
||||||
slider.value = newValue
|
slider.value = newValue;
|
||||||
slider.sliderValueChanged(newValue)
|
slider.sliderValueChanged(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (sliderMouseArea.isDragging && slider.enabled) {
|
if (sliderMouseArea.isDragging && slider.enabled) {
|
||||||
sliderMouseArea.isDragging = false
|
sliderMouseArea.isDragging = false;
|
||||||
slider.sliderDragFinished(slider.value)
|
slider.sliderDragFinished(slider.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right icon
|
// Right icon
|
||||||
@@ -197,6 +208,9 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: slider.rightIcon.length > 0
|
visible: slider.rightIcon.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -20,7 +20,34 @@ PanelWindow {
|
|||||||
signal confirmed(string value)
|
signal confirmed(string value)
|
||||||
signal cancelled()
|
signal cancelled()
|
||||||
|
|
||||||
|
function showDialog(title, subtitle, placeholder, isPass, confirmText, cancelText) {
|
||||||
|
dialogTitle = title || "Input Required";
|
||||||
|
dialogSubtitle = subtitle || "Please enter the required information";
|
||||||
|
inputPlaceholder = placeholder || "Enter text";
|
||||||
|
isPassword = isPass || false;
|
||||||
|
confirmButtonText = confirmText || "Confirm";
|
||||||
|
cancelButtonText = cancelText || "Cancel";
|
||||||
|
inputValue = "";
|
||||||
|
dialogVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideDialog() {
|
||||||
|
dialogVisible = false;
|
||||||
|
inputValue = "";
|
||||||
|
}
|
||||||
|
|
||||||
visible: dialogVisible
|
visible: dialogVisible
|
||||||
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: dialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||||
|
color: "transparent"
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
textInput.forceActiveFocus();
|
||||||
|
textInput.text = inputValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
@@ -28,54 +55,27 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: dialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
textInput.forceActiveFocus()
|
|
||||||
textInput.text = inputValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDialog(title, subtitle, placeholder, isPass, confirmText, cancelText) {
|
|
||||||
dialogTitle = title || "Input Required"
|
|
||||||
dialogSubtitle = subtitle || "Please enter the required information"
|
|
||||||
inputPlaceholder = placeholder || "Enter text"
|
|
||||||
isPassword = isPass || false
|
|
||||||
confirmButtonText = confirmText || "Confirm"
|
|
||||||
cancelButtonText = cancelText || "Cancel"
|
|
||||||
inputValue = ""
|
|
||||||
dialogVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideDialog() {
|
|
||||||
dialogVisible = false
|
|
||||||
inputValue = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(0, 0, 0, 0.5)
|
color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
opacity: dialogVisible ? 1.0 : 0.0
|
opacity: dialogVisible ? 1 : 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
inputDialog.cancelled();
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
inputDialog.cancelled()
|
|
||||||
hideDialog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -86,23 +86,8 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: dialogVisible ? 1 : 0
|
||||||
opacity: dialogVisible ? 1.0 : 0.0
|
scale: dialogVisible ? 1 : 0.9
|
||||||
scale: dialogVisible ? 1.0 : 0.9
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -133,6 +118,7 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -151,15 +137,18 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closeDialogArea
|
id: closeDialogArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
inputDialog.cancelled()
|
inputDialog.cancelled();
|
||||||
hideDialog()
|
hideDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text input
|
// Text input
|
||||||
@@ -173,6 +162,7 @@ PanelWindow {
|
|||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: textInput
|
id: textInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -181,6 +171,18 @@ PanelWindow {
|
|||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
cursorVisible: activeFocus
|
cursorVisible: activeFocus
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
onTextChanged: {
|
||||||
|
inputValue = text;
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
inputDialog.confirmed(inputValue);
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (dialogVisible)
|
||||||
|
forceActiveFocus();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -191,29 +193,16 @@ PanelWindow {
|
|||||||
visible: parent.text.length === 0
|
visible: parent.text.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
inputValue = text
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
inputDialog.confirmed(inputValue)
|
|
||||||
hideDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (dialogVisible) {
|
|
||||||
forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
textInput.forceActiveFocus()
|
textInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show password checkbox (only visible for password inputs)
|
// Show password checkbox (only visible for password inputs)
|
||||||
@@ -223,6 +212,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: showPasswordCheckbox
|
id: showPasswordCheckbox
|
||||||
|
|
||||||
property bool checked: false
|
property bool checked: false
|
||||||
|
|
||||||
width: 20
|
width: 20
|
||||||
@@ -246,9 +236,10 @@ PanelWindow {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
showPasswordCheckbox.checked = !showPasswordCheckbox.checked
|
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -257,6 +248,7 @@ PanelWindow {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
@@ -279,6 +271,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: cancelText
|
id: cancelText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: cancelButtonText
|
text: cancelButtonText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -288,14 +281,16 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cancelArea
|
id: cancelArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
inputDialog.cancelled()
|
inputDialog.cancelled();
|
||||||
hideDialog()
|
hideDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -304,10 +299,11 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: confirmArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
color: confirmArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
enabled: inputValue.length > 0
|
enabled: inputValue.length > 0
|
||||||
opacity: enabled ? 1.0 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: confirmText
|
id: confirmText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: confirmButtonText
|
text: confirmButtonText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -317,13 +313,14 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: confirmArea
|
id: confirmArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: {
|
onClicked: {
|
||||||
inputDialog.confirmed(inputValue)
|
inputDialog.confirmed(inputValue);
|
||||||
hideDialog()
|
hideDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +329,33 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -13,14 +13,11 @@ PanelWindow {
|
|||||||
property bool notificationHistoryVisible: false
|
property bool notificationHistoryVisible: false
|
||||||
|
|
||||||
visible: notificationHistoryVisible
|
visible: notificationHistoryVisible
|
||||||
|
|
||||||
implicitWidth: 400
|
implicitWidth: 400
|
||||||
implicitHeight: 500
|
implicitHeight: 500
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -34,7 +31,7 @@ PanelWindow {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notificationHistoryVisible = false
|
notificationHistoryVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,43 +44,65 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 0.5
|
border.width: 0.5
|
||||||
|
opacity: notificationHistoryVisible ? 1 : 0
|
||||||
// Animation
|
// Animation
|
||||||
transform: [
|
transform: [
|
||||||
Scale {
|
Scale {
|
||||||
id: scaleTransform
|
id: scaleTransform
|
||||||
origin.x: 400 // Use fixed width since popup is 400px wide
|
|
||||||
|
origin.x: 400 // Use fixed width since popup is 400px wide
|
||||||
origin.y: 0
|
origin.y: 0
|
||||||
xScale: notificationHistoryVisible ? 1.0 : 0.95
|
xScale: notificationHistoryVisible ? 1 : 0.95
|
||||||
yScale: notificationHistoryVisible ? 1.0 : 0.8
|
yScale: notificationHistoryVisible ? 1 : 0.8
|
||||||
},
|
},
|
||||||
Translate {
|
Translate {
|
||||||
id: translateTransform
|
id: translateTransform
|
||||||
|
|
||||||
x: notificationHistoryVisible ? 0 : 15
|
x: notificationHistoryVisible ? 0 : 15
|
||||||
y: notificationHistoryVisible ? 0 : -30
|
y: notificationHistoryVisible ? 0 : -30
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
opacity: notificationHistoryVisible ? 1.0 : 0.0
|
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "visible"
|
name: "visible"
|
||||||
when: notificationHistoryVisible
|
when: notificationHistoryVisible
|
||||||
PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 }
|
|
||||||
PropertyChanges { target: translateTransform; x: 0; y: 0 }
|
PropertyChanges {
|
||||||
|
target: scaleTransform
|
||||||
|
xScale: 1
|
||||||
|
yScale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: translateTransform
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "hidden"
|
name: "hidden"
|
||||||
when: !notificationHistoryVisible
|
when: !notificationHistoryVisible
|
||||||
PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 }
|
|
||||||
PropertyChanges { target: translateTransform; x: 15; y: -30 }
|
PropertyChanges {
|
||||||
|
target: scaleTransform
|
||||||
|
xScale: 0.95
|
||||||
|
yScale: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: translateTransform
|
||||||
|
x: 15
|
||||||
|
y: -30
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
from: "*"; to: "*"
|
from: "*"
|
||||||
|
to: "*"
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
targets: [scaleTransform, translateTransform]
|
targets: [scaleTransform, translateTransform]
|
||||||
@@ -91,22 +110,18 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent clicks from propagating to background
|
// Prevent clicks from propagating to background
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Stop propagation - do nothing
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// Stop propagation - do nothing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,14 +152,8 @@ PanelWindow {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: NotificationService.notifications.length > 0
|
visible: NotificationService.notifications.length > 0
|
||||||
|
color: clearArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.surfaceContainer
|
||||||
color: clearArea.containsMouse ?
|
border.color: clearArea.containsMouse ? Theme.primary : Theme.outline
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
Theme.surfaceContainer
|
|
||||||
|
|
||||||
border.color: clearArea.containsMouse ?
|
|
||||||
Theme.primary :
|
|
||||||
Theme.outline
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -166,14 +175,15 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: clearArea
|
id: clearArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: NotificationService.clearAllNotifications()
|
onClicked: NotificationService.clearAllNotifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +192,7 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on border.color {
|
Behavior on border.color {
|
||||||
@@ -189,8 +200,11 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification List
|
// Notification List
|
||||||
@@ -203,6 +217,8 @@ PanelWindow {
|
|||||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
// Quick reply height
|
||||||
|
|
||||||
model: NotificationService.groupedNotifications
|
model: NotificationService.groupedNotifications
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
interactive: true
|
interactive: true
|
||||||
@@ -220,13 +236,16 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "height"
|
properties: "height"
|
||||||
from: 0
|
from: 0
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remove: Transition {
|
remove: Transition {
|
||||||
@@ -235,6 +254,7 @@ PanelWindow {
|
|||||||
PauseAnimation {
|
PauseAnimation {
|
||||||
duration: 50
|
duration: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "opacity"
|
properties: "opacity"
|
||||||
@@ -242,14 +262,18 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
properties: "height,anchors.topMargin,anchors.bottomMargin"
|
properties: "height,anchors.topMargin,anchors.bottomMargin"
|
||||||
to: 0
|
to: 0
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
displaced: Transition {
|
displaced: Transition {
|
||||||
@@ -258,6 +282,7 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add move transition for internal content changes
|
// Add move transition for internal content changes
|
||||||
@@ -267,37 +292,35 @@ PanelWindow {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
readonly property bool expanded: NotificationService.expandedGroups[modelData.key] || false
|
readonly property bool expanded: NotificationService.expandedGroups[modelData.key] || false
|
||||||
|
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
height: {
|
height: {
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
// Calculate expanded height: header (48) + spacing (16) + individual notifications
|
// Calculate expanded height: header (48) + spacing (16) + individual notifications
|
||||||
let headerHeight = 48 + Theme.spacingM
|
let headerHeight = 48 + Theme.spacingM;
|
||||||
let notificationHeight = modelData.notifications.length * (60 + Theme.spacingS) // Each notification ~60px + spacing
|
let notificationHeight = modelData.notifications.length * (60 + Theme.spacingS); // Each notification ~60px + spacing
|
||||||
let totalExpandedHeight = headerHeight + notificationHeight + Theme.spacingL * 2
|
let totalExpandedHeight = headerHeight + notificationHeight + Theme.spacingL * 2;
|
||||||
return Math.max(totalExpandedHeight, 200) // Minimum expanded height
|
return Math.max(totalExpandedHeight, 200); // Minimum expanded height
|
||||||
} else {
|
} else {
|
||||||
// Collapsed height: icon + content + quick reply (if any)
|
// Collapsed height: icon + content + quick reply (if any)
|
||||||
let collapsedHeight = 72 + Theme.spacingS * 2 // Header height + spacing
|
let collapsedHeight = 72 + Theme.spacingS * 2;
|
||||||
if (modelData.latestNotification.notification.hasInlineReply) {
|
// Header height + spacing
|
||||||
collapsedHeight += 36 + Theme.spacingS // Quick reply height
|
if (modelData.latestNotification.notification.hasInlineReply)
|
||||||
}
|
collapsedHeight += 36 + Theme.spacingS;
|
||||||
return collapsedHeight + Theme.spacingL * 2
|
|
||||||
|
return collapsedHeight + Theme.spacingL * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
border.color: modelData.latestNotification.urgency === 2 ?
|
border.color: modelData.latestNotification.urgency === 2 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) :
|
|
||||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
|
||||||
border.width: modelData.latestNotification.urgency === 2 ? 2 : 1
|
border.width: modelData.latestNotification.urgency === 2 ? 2 : 1
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
// Priority indicator for urgent notifications
|
// Priority indicator for urgent notifications
|
||||||
@@ -312,19 +335,10 @@ PanelWindow {
|
|||||||
visible: modelData.latestNotification.urgency === 2
|
visible: modelData.latestNotification.urgency === 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
SequentialAnimation {
|
|
||||||
PauseAnimation { duration: 25 }
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapsed view - shows app header and latest notification
|
// Collapsed view - shows app header and latest notification
|
||||||
Column {
|
Column {
|
||||||
id: collapsedContent
|
id: collapsedContent
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -340,6 +354,7 @@ PanelWindow {
|
|||||||
// App icon with proper fallback handling
|
// App icon with proper fallback handling
|
||||||
Item {
|
Item {
|
||||||
id: iconContainer
|
id: iconContainer
|
||||||
|
|
||||||
width: 48
|
width: 48
|
||||||
height: 48
|
height: 48
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -358,34 +373,34 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 6
|
anchors.margins: 6
|
||||||
source: {
|
source: {
|
||||||
if (modelData.latestNotification.appIcon && modelData.latestNotification.appIcon !== "") {
|
if (modelData.latestNotification.appIcon && modelData.latestNotification.appIcon !== "")
|
||||||
return Quickshell.iconPath(modelData.latestNotification.appIcon, "")
|
return Quickshell.iconPath(modelData.latestNotification.appIcon, "");
|
||||||
}
|
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
visible: status === Image.Ready
|
visible: status === Image.Ready
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === Image.Error || status === Image.Null || source === "") {
|
if (status === Image.Error || status === Image.Null || source === "")
|
||||||
fallbackIcon.visible = true
|
fallbackIcon.visible = true;
|
||||||
} else if (status === Image.Ready) {
|
else if (status === Image.Ready)
|
||||||
fallbackIcon.visible = false
|
fallbackIcon.visible = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: fallbackIcon
|
id: fallbackIcon
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: true
|
visible: true
|
||||||
text: {
|
text: {
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count badge for multiple notifications
|
// Count badge for multiple notifications
|
||||||
@@ -407,7 +422,9 @@ PanelWindow {
|
|||||||
font.pixelSize: 9
|
font.pixelSize: 9
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Content area with proper spacing
|
// Content area with proper spacing
|
||||||
@@ -424,11 +441,10 @@ PanelWindow {
|
|||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: {
|
text: {
|
||||||
if (modelData.latestNotification.timeStr.length > 0) {
|
if (modelData.latestNotification.timeStr.length > 0)
|
||||||
return modelData.appName + " • " + modelData.latestNotification.timeStr
|
return modelData.appName + " • " + modelData.latestNotification.timeStr;
|
||||||
} else {
|
else
|
||||||
return modelData.appName
|
return modelData.appName;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -460,11 +476,13 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Controls with fixed positioning
|
// Controls with fixed positioning
|
||||||
Item {
|
Item {
|
||||||
id: controlsContainer
|
id: controlsContainer
|
||||||
|
|
||||||
width: 72
|
width: 72
|
||||||
height: 32
|
height: 32
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -475,9 +493,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
color: expandArea.containsMouse ?
|
color: expandArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
visible: modelData.count > 1
|
visible: modelData.count > 1
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -493,16 +509,20 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: expandArea
|
id: expandArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -510,9 +530,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: dismissArea.containsMouse ?
|
color: dismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -524,13 +542,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dismissArea
|
id: dismissArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissGroup(modelData.key)
|
onClicked: NotificationService.dismissGroup(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced quick reply for conversations
|
// Enhanced quick reply for conversations
|
||||||
@@ -549,20 +571,24 @@ PanelWindow {
|
|||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: quickReplyField
|
id: quickReplyField
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
placeholderText: modelData.latestNotification.notification.inlineReplyPlaceholder || "Quick reply..."
|
placeholderText: modelData.latestNotification.notification.inlineReplyPlaceholder || "Quick reply..."
|
||||||
background: Item {}
|
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
modelData.latestNotification.notification.sendInlineReply(text)
|
modelData.latestNotification.notification.sendInlineReply(text);
|
||||||
text = ""
|
text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -586,8 +612,8 @@ PanelWindow {
|
|||||||
enabled: quickReplyField.text.length > 0
|
enabled: quickReplyField.text.length > 0
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
modelData.latestNotification.notification.sendInlineReply(quickReplyField.text)
|
modelData.latestNotification.notification.sendInlineReply(quickReplyField.text);
|
||||||
quickReplyField.text = ""
|
quickReplyField.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,14 +622,19 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expanded view - shows all notifications stacked
|
// Expanded view - shows all notifications stacked
|
||||||
Column {
|
Column {
|
||||||
id: expandedContent
|
id: expandedContent
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -638,13 +669,14 @@ PanelWindow {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !modelData.latestNotification.appIcon || modelData.latestNotification.appIcon === ""
|
visible: !modelData.latestNotification.appIcon || modelData.latestNotification.appIcon === ""
|
||||||
text: {
|
text: {
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -668,9 +700,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
color: collapseArea.containsMouse ?
|
color: collapseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -682,11 +712,13 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: collapseArea
|
id: collapseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -694,9 +726,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: dismissAllArea.containsMouse ?
|
color: dismissAllArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -708,13 +738,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dismissAllArea
|
id: dismissAllArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissGroup(modelData.key)
|
onClicked: NotificationService.dismissGroup(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Individual notifications
|
// Individual notifications
|
||||||
@@ -732,14 +766,13 @@ PanelWindow {
|
|||||||
height: Math.max(48, 32 + contentColumn.height + Theme.spacingM * 2) // 32 for icon height, plus content
|
height: Math.max(48, 32 + contentColumn.height + Theme.spacingM * 2) // 32 for icon height, plus content
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
||||||
border.color: modelData.urgency === 2 ?
|
border.color: modelData.urgency === 2 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent"
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) :
|
|
||||||
"transparent"
|
|
||||||
border.width: modelData.urgency === 2 ? 1 : 0
|
border.width: modelData.urgency === 2 ? 1 : 0
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: notifContent
|
id: notifContent
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -759,13 +792,14 @@ PanelWindow {
|
|||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
text: {
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -774,9 +808,7 @@ PanelWindow {
|
|||||||
radius: 12
|
radius: 12
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: individualDismissArea.containsMouse ?
|
color: individualDismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -788,15 +820,18 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: individualDismissArea
|
id: individualDismissArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissNotification(modelData)
|
onClicked: NotificationService.dismissNotification(modelData)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 44
|
anchors.leftMargin: 44
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -840,20 +875,24 @@ PanelWindow {
|
|||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: replyField
|
id: replyField
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
placeholderText: modelData.notification.inlineReplyPlaceholder || "Reply..."
|
placeholderText: modelData.notification.inlineReplyPlaceholder || "Reply..."
|
||||||
background: Item {}
|
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: 11
|
font.pixelSize: 11
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
modelData.notification.sendInlineReply(text)
|
modelData.notification.sendInlineReply(text);
|
||||||
text = ""
|
text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -875,17 +914,25 @@ PanelWindow {
|
|||||||
enabled: replyField.text.length > 0
|
enabled: replyField.text.length > 0
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
modelData.notification.sendInlineReply(replyField.text)
|
modelData.notification.sendInlineReply(replyField.text);
|
||||||
replyField.text = ""
|
replyField.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tap to expand for collapsed groups
|
// Tap to expand for collapsed groups
|
||||||
@@ -895,7 +942,24 @@ PanelWindow {
|
|||||||
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
onClicked: NotificationService.toggleGroupExpansion(modelData.key)
|
||||||
z: -1
|
z: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 25
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty state
|
// Empty state
|
||||||
@@ -936,10 +1000,23 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside to close
|
// Click outside to close
|
||||||
@@ -948,4 +1025,5 @@ PanelWindow {
|
|||||||
z: -1
|
z: -1
|
||||||
onClicked: notificationHistoryVisible = false
|
onClicked: notificationHistoryVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: notificationPopup
|
id: notificationPopup
|
||||||
objectName: "notificationPopup"
|
|
||||||
|
|
||||||
visible: NotificationService.groupedPopups.length > 0
|
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
|
|
||||||
margins {
|
|
||||||
top: Theme.barHeight
|
|
||||||
right: 12
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: 400
|
|
||||||
implicitHeight: notificationsList.height + 32
|
|
||||||
|
|
||||||
// Expose key child objects for testing
|
// Expose key child objects for testing
|
||||||
// Expose the currently visible quickReplyField for testing
|
// Expose the currently visible quickReplyField for testing
|
||||||
@@ -41,8 +19,28 @@ PanelWindow {
|
|||||||
// Expose the currently visible hoverArea for testing
|
// Expose the currently visible hoverArea for testing
|
||||||
property MouseArea hoverArea: null
|
property MouseArea hoverArea: null
|
||||||
|
|
||||||
|
objectName: "notificationPopup"
|
||||||
|
visible: NotificationService.groupedPopups.length > 0
|
||||||
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: 400
|
||||||
|
implicitHeight: notificationsList.height + 32
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
top: Theme.barHeight
|
||||||
|
right: 12
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: notificationsList
|
id: notificationsList
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
@@ -55,7 +53,6 @@ PanelWindow {
|
|||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
// Context detection for popup
|
// Context detection for popup
|
||||||
readonly property bool isPopupContext: true
|
readonly property bool isPopupContext: true
|
||||||
readonly property bool expanded: NotificationService.expandedGroups[modelData.key] || false
|
readonly property bool expanded: NotificationService.expandedGroups[modelData.key] || false
|
||||||
@@ -65,61 +62,32 @@ PanelWindow {
|
|||||||
let calculatedHeight;
|
let calculatedHeight;
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
// Calculate expanded height properly: header (48) + spacing + notifications
|
// Calculate expanded height properly: header (48) + spacing + notifications
|
||||||
let headerHeight = 48 + Theme.spacingM
|
let headerHeight = 48 + Theme.spacingM;
|
||||||
let maxNotificationsInPopup = Math.min(modelData.notifications.length, 5)
|
let maxNotificationsInPopup = Math.min(modelData.notifications.length, 5);
|
||||||
let notificationHeight = maxNotificationsInPopup * (60 + Theme.spacingS)
|
let notificationHeight = maxNotificationsInPopup * (60 + Theme.spacingS);
|
||||||
calculatedHeight = headerHeight + notificationHeight + Theme.spacingL * 2
|
calculatedHeight = headerHeight + notificationHeight + Theme.spacingL * 2;
|
||||||
} else {
|
} else {
|
||||||
// Collapsed height: header (72) + quick reply if present
|
// Collapsed height: header (72) + quick reply if present
|
||||||
calculatedHeight = 72 + Theme.spacingS * 2
|
calculatedHeight = 72 + Theme.spacingS * 2;
|
||||||
if (modelData.latestNotification.notification.hasInlineReply) {
|
if (modelData.latestNotification.notification.hasInlineReply)
|
||||||
calculatedHeight += 36 + Theme.spacingS
|
calculatedHeight += 36 + Theme.spacingS;
|
||||||
}
|
|
||||||
calculatedHeight += Theme.spacingL * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add extra height for single notifications in popup context
|
calculatedHeight += Theme.spacingL * 2;
|
||||||
if (isPopupContext && modelData.count === 1) {
|
|
||||||
calculatedHeight += 12;
|
|
||||||
}
|
}
|
||||||
|
// Add extra height for single notifications in popup context
|
||||||
|
if (isPopupContext && modelData.count === 1)
|
||||||
|
calculatedHeight += 12;
|
||||||
|
|
||||||
return calculatedHeight;
|
return calculatedHeight;
|
||||||
}
|
}
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
border.color: modelData.latestNotification.urgency === 2 ?
|
border.color: modelData.latestNotification.urgency === 2 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) :
|
|
||||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
|
||||||
border.width: modelData.latestNotification.urgency === 2 ? 2 : 1
|
border.width: modelData.latestNotification.urgency === 2 ? 2 : 1
|
||||||
|
|
||||||
// Stabilize layout during content changes
|
// Stabilize layout during content changes
|
||||||
clip: true
|
clip: true
|
||||||
|
opacity: notificationPopup.visible ? 1 : 0
|
||||||
// Smooth popup animations
|
scale: notificationPopup.visible ? 1 : 0.98
|
||||||
transform: Translate {
|
|
||||||
x: notificationPopup.visible ? 0 : 400
|
|
||||||
Behavior on x {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 350
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: notificationPopup.visible ? 1.0 : 0.0
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 300
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scale: notificationPopup.visible ? 1.0 : 0.98
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 350
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority indicator for urgent notifications
|
// Priority indicator for urgent notifications
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -133,20 +101,10 @@ PanelWindow {
|
|||||||
visible: modelData.latestNotification.urgency === 2
|
visible: modelData.latestNotification.urgency === 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
enabled: !isPopupContext // Disable automatic height animation in popup to prevent glitches
|
|
||||||
SequentialAnimation {
|
|
||||||
PauseAnimation { duration: 25 }
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapsed view - shows app header and latest notification
|
// Collapsed view - shows app header and latest notification
|
||||||
Column {
|
Column {
|
||||||
id: collapsedContent
|
id: collapsedContent
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -162,9 +120,10 @@ PanelWindow {
|
|||||||
// Round app icon with proper API usage
|
// Round app icon with proper API usage
|
||||||
Item {
|
Item {
|
||||||
id: iconContainer
|
id: iconContainer
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Expose this iconContainer to the root for testing if visible
|
// Expose this iconContainer to the root for testing if visible
|
||||||
notificationPopup.iconContainer = iconContainer
|
notificationPopup.iconContainer = iconContainer;
|
||||||
}
|
}
|
||||||
width: 48
|
width: 48
|
||||||
height: 48
|
height: 48
|
||||||
@@ -184,36 +143,36 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 6
|
anchors.margins: 6
|
||||||
source: {
|
source: {
|
||||||
if (modelData.latestNotification.appIcon && modelData.latestNotification.appIcon !== "") {
|
if (modelData.latestNotification.appIcon && modelData.latestNotification.appIcon !== "")
|
||||||
return Quickshell.iconPath(modelData.latestNotification.appIcon, "")
|
return Quickshell.iconPath(modelData.latestNotification.appIcon, "");
|
||||||
}
|
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
visible: status === Image.Ready
|
visible: status === Image.Ready
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === Image.Error || status === Image.Null || source === "") {
|
if (status === Image.Error || status === Image.Null || source === "")
|
||||||
fallbackIcon.visible = true
|
fallbackIcon.visible = true;
|
||||||
} else if (status === Image.Ready) {
|
else if (status === Image.Ready)
|
||||||
fallbackIcon.visible = false
|
fallbackIcon.visible = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback icon - show by default, hide when real icon loads
|
// Fallback icon - show by default, hide when real icon loads
|
||||||
Text {
|
Text {
|
||||||
id: fallbackIcon
|
id: fallbackIcon
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: true // Start visible, hide when real icon loads
|
visible: true // Start visible, hide when real icon loads
|
||||||
text: {
|
text: {
|
||||||
// Use first letter of app name as fallback
|
// Use first letter of app name as fallback
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count badge for multiple notifications - smaller circle
|
// Count badge for multiple notifications - smaller circle
|
||||||
@@ -235,7 +194,9 @@ PanelWindow {
|
|||||||
font.pixelSize: 9
|
font.pixelSize: 9
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// App info and latest notification content
|
// App info and latest notification content
|
||||||
@@ -252,11 +213,10 @@ PanelWindow {
|
|||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: {
|
text: {
|
||||||
if (modelData.latestNotification.timeStr.length > 0) {
|
if (modelData.latestNotification.timeStr.length > 0)
|
||||||
return modelData.appName + " • " + modelData.latestNotification.timeStr
|
return modelData.appName + " • " + modelData.latestNotification.timeStr;
|
||||||
} else {
|
else
|
||||||
return modelData.appName
|
return modelData.appName;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
@@ -269,7 +229,7 @@ PanelWindow {
|
|||||||
Text {
|
Text {
|
||||||
text: modelData.latestNotification.summary
|
text: modelData.latestNotification.summary
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: Theme.fontSizeMedium + 1 // Slightly larger for emphasis
|
font.pixelSize: Theme.fontSizeMedium + 1 // Slightly larger for emphasis
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
@@ -284,15 +244,17 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: modelData.count > 1 ? 1 : 2 // More space for single notifications
|
maximumLineCount: modelData.count > 1 ? 1 : 2 // More space for single notifications
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand/dismiss controls - use anchored layout for stability
|
// Expand/dismiss controls - use anchored layout for stability
|
||||||
Item {
|
Item {
|
||||||
id: controlsContainer
|
id: controlsContainer
|
||||||
|
|
||||||
width: 72
|
width: 72
|
||||||
height: 32
|
height: 32
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -303,9 +265,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
color: expandArea.containsMouse ?
|
color: expandArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
visible: modelData.count > 1
|
visible: modelData.count > 1
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -321,21 +281,25 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
// ...existing code...
|
// ...existing code...
|
||||||
id: expandArea
|
id: expandArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Expand clicked - pausing timer")
|
console.log("Expand clicked - pausing timer");
|
||||||
dismissTimer.stop()
|
dismissTimer.stop();
|
||||||
NotificationService.toggleGroupExpansion(modelData.key)
|
NotificationService.toggleGroupExpansion(modelData.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -343,9 +307,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: dismissArea.containsMouse ?
|
color: dismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -357,13 +319,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dismissArea
|
id: dismissArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissGroup(modelData.key)
|
onClicked: NotificationService.dismissGroup(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quick reply for conversations (only if latest notification supports it)
|
// Quick reply for conversations (only if latest notification supports it)
|
||||||
@@ -382,20 +348,24 @@ PanelWindow {
|
|||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: quickReplyField
|
id: quickReplyField
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
placeholderText: modelData.latestNotification.notification.inlineReplyPlaceholder || "Quick reply..."
|
placeholderText: modelData.latestNotification.notification.inlineReplyPlaceholder || "Quick reply..."
|
||||||
background: Item {}
|
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
modelData.latestNotification.notification.sendInlineReply(text)
|
modelData.latestNotification.notification.sendInlineReply(text);
|
||||||
text = ""
|
text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -419,8 +389,8 @@ PanelWindow {
|
|||||||
enabled: quickReplyField.text.length > 0
|
enabled: quickReplyField.text.length > 0
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
modelData.latestNotification.notification.sendInlineReply(quickReplyField.text)
|
modelData.latestNotification.notification.sendInlineReply(quickReplyField.text);
|
||||||
quickReplyField.text = ""
|
quickReplyField.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,17 +399,22 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expanded view - shows all notifications stacked
|
// Expanded view - shows all notifications stacked
|
||||||
Column {
|
Column {
|
||||||
id: expandedContent
|
id: expandedContent
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Expose this expandedContent to the root for testing if visible
|
// Expose this expandedContent to the root for testing if visible
|
||||||
notificationPopup.expandedContent = expandedContent
|
notificationPopup.expandedContent = expandedContent;
|
||||||
}
|
}
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -477,13 +452,14 @@ PanelWindow {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !modelData.latestNotification.appIcon || modelData.latestNotification.appIcon === ""
|
visible: !modelData.latestNotification.appIcon || modelData.latestNotification.appIcon === ""
|
||||||
text: {
|
text: {
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// App name and count badge - centered area
|
// App name and count badge - centered area
|
||||||
@@ -509,9 +485,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
color: collapseArea.containsMouse ?
|
color: collapseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -523,15 +497,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: collapseArea
|
id: collapseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Expand clicked - pausing timer")
|
console.log("Expand clicked - pausing timer");
|
||||||
dismissTimer.stop()
|
dismissTimer.stop();
|
||||||
NotificationService.toggleGroupExpansion(modelData.key)
|
NotificationService.toggleGroupExpansion(modelData.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -539,9 +515,7 @@ PanelWindow {
|
|||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: dismissAllArea.containsMouse ?
|
color: dismissAllArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -553,13 +527,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: dismissAllArea
|
id: dismissAllArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissGroup(modelData.key)
|
onClicked: NotificationService.dismissGroup(modelData.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stacked individual notifications with smooth transitions
|
// Stacked individual notifications with smooth transitions
|
||||||
@@ -577,16 +555,14 @@ PanelWindow {
|
|||||||
height: notifContent.height + Theme.spacingM * 2
|
height: notifContent.height + Theme.spacingM * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
|
||||||
border.color: modelData.urgency === 2 ?
|
border.color: modelData.urgency === 2 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent"
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) :
|
|
||||||
"transparent"
|
|
||||||
border.width: modelData.urgency === 2 ? 1 : 0
|
border.width: modelData.urgency === 2 ? 1 : 0
|
||||||
|
|
||||||
// Stabilize layout during dismiss operations
|
// Stabilize layout during dismiss operations
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: notifContent
|
id: notifContent
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -617,13 +593,14 @@ PanelWindow {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !modelData.appIcon || modelData.appIcon === ""
|
visible: !modelData.appIcon || modelData.appIcon === ""
|
||||||
text: {
|
text: {
|
||||||
const appName = modelData.appName || "?"
|
const appName = modelData.appName || "?";
|
||||||
return appName.charAt(0).toUpperCase()
|
return appName.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Individual dismiss button - fixed position on right
|
// Individual dismiss button - fixed position on right
|
||||||
@@ -633,9 +610,7 @@ PanelWindow {
|
|||||||
radius: 12
|
radius: 12
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: individualDismissArea.containsMouse ?
|
color: individualDismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -647,16 +622,19 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: individualDismissArea
|
id: individualDismissArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: NotificationService.dismissNotification(modelData)
|
onClicked: NotificationService.dismissNotification(modelData)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification content - fills space between icon and dismiss button
|
// Notification content - fills space between icon and dismiss button
|
||||||
Column {
|
Column {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 44
|
anchors.leftMargin: 44
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -702,20 +680,24 @@ PanelWindow {
|
|||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: replyField
|
id: replyField
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
placeholderText: modelData.notification.inlineReplyPlaceholder || "Reply..."
|
placeholderText: modelData.notification.inlineReplyPlaceholder || "Reply..."
|
||||||
background: Item {}
|
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.pixelSize: 11
|
font.pixelSize: 11
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
modelData.notification.sendInlineReply(text)
|
modelData.notification.sendInlineReply(text);
|
||||||
text = ""
|
text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -737,69 +719,129 @@ PanelWindow {
|
|||||||
enabled: replyField.text.length > 0
|
enabled: replyField.text.length > 0
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
modelData.notification.sendInlineReply(replyField.text)
|
modelData.notification.sendInlineReply(replyField.text);
|
||||||
replyField.text = ""
|
replyField.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover to pause auto-dismiss - MUST be properly configured
|
// Hover to pause auto-dismiss - MUST be properly configured
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: hoverArea
|
id: hoverArea
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Expose this hoverArea to the root for testing if visible
|
// Expose this hoverArea to the root for testing if visible
|
||||||
notificationPopup.hoverArea = hoverArea
|
notificationPopup.hoverArea = hoverArea;
|
||||||
}
|
}
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
z: 10 // Higher z-order to ensure hover detection
|
z: 10 // Higher z-order to ensure hover detection
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
|
|
||||||
onEntered: {
|
onEntered: {
|
||||||
console.log("Notification hover entered - pausing timer")
|
console.log("Notification hover entered - pausing timer");
|
||||||
dismissTimer.stop()
|
dismissTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: {
|
onExited: {
|
||||||
console.log("Notification hover exited - resuming timer")
|
console.log("Notification hover exited - resuming timer");
|
||||||
if (modelData.latestNotification.popup && !expanded) {
|
if (modelData.latestNotification.popup && !expanded)
|
||||||
dismissTimer.restart()
|
dismissTimer.restart();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-dismiss timer - properly pauses on hover
|
// Auto-dismiss timer - properly pauses on hover
|
||||||
Timer {
|
Timer {
|
||||||
id: dismissTimer
|
id: dismissTimer
|
||||||
|
|
||||||
running: modelData.latestNotification.popup && !expanded
|
running: modelData.latestNotification.popup && !expanded
|
||||||
interval: modelData.latestNotification.notification.expireTimeout > 0 ?
|
interval: modelData.latestNotification.notification.expireTimeout > 0 ? modelData.latestNotification.notification.expireTimeout * 1000 : 5000
|
||||||
modelData.latestNotification.notification.expireTimeout * 1000 : 5000
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
console.log("Timer triggered - hover state:", hoverArea.containsMouse, "expanded:", expanded)
|
console.log("Timer triggered - hover state:", hoverArea.containsMouse, "expanded:", expanded);
|
||||||
if (!hoverArea.containsMouse && !expanded) {
|
if (!hoverArea.containsMouse && !expanded) {
|
||||||
console.log("Dismissing notification")
|
console.log("Dismissing notification");
|
||||||
modelData.latestNotification.popup = false
|
modelData.latestNotification.popup = false;
|
||||||
} else {
|
} else {
|
||||||
console.log("Conditions not met - not dismissing")
|
console.log("Conditions not met - not dismissing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Smooth popup animations
|
||||||
|
transform: Translate {
|
||||||
|
x: notificationPopup.visible ? 0 : 400
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 350
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 350
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
enabled: !isPopupContext // Disable automatic height animation in popup to prevent glitches
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 25
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// Smooth height animation
|
// Smooth height animation
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -14,15 +14,35 @@ PanelWindow {
|
|||||||
property string powerConfirmTitle: ""
|
property string powerConfirmTitle: ""
|
||||||
property string powerConfirmMessage: ""
|
property string powerConfirmMessage: ""
|
||||||
|
|
||||||
visible: powerConfirmVisible
|
function executePowerAction(action) {
|
||||||
|
console.log("Executing power action:", action);
|
||||||
|
let command = [];
|
||||||
|
switch (action) {
|
||||||
|
case "logout":
|
||||||
|
command = ["niri", "msg", "action", "quit", "-s"];
|
||||||
|
break;
|
||||||
|
case "suspend":
|
||||||
|
command = ["systemctl", "suspend"];
|
||||||
|
break;
|
||||||
|
case "reboot":
|
||||||
|
command = ["systemctl", "reboot"];
|
||||||
|
break;
|
||||||
|
case "poweroff":
|
||||||
|
command = ["systemctl", "poweroff"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (command.length > 0) {
|
||||||
|
powerActionProcess.command = command;
|
||||||
|
powerActionProcess.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: powerConfirmVisible
|
||||||
implicitWidth: 400
|
implicitWidth: 400
|
||||||
implicitHeight: 300
|
implicitHeight: 300
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -47,23 +67,8 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: powerConfirmVisible ? 1 : 0
|
||||||
opacity: powerConfirmVisible ? 1.0 : 0.0
|
scale: powerConfirmVisible ? 1 : 0.9
|
||||||
scale: powerConfirmVisible ? 1.0 : 0.9
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -75,10 +80,13 @@ PanelWindow {
|
|||||||
text: powerConfirmTitle
|
text: powerConfirmTitle
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: {
|
color: {
|
||||||
switch(powerConfirmAction) {
|
switch (powerConfirmAction) {
|
||||||
case "poweroff": return Theme.error
|
case "poweroff":
|
||||||
case "reboot": return Theme.warning
|
return Theme.error;
|
||||||
default: return Theme.surfaceText
|
case "reboot":
|
||||||
|
return Theme.warning;
|
||||||
|
default:
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -96,7 +104,9 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { height: Theme.spacingL }
|
Item {
|
||||||
|
height: Theme.spacingL
|
||||||
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
Row {
|
Row {
|
||||||
@@ -120,13 +130,15 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerConfirmVisible = false
|
powerConfirmVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm button
|
// Confirm button
|
||||||
@@ -135,15 +147,19 @@ PanelWindow {
|
|||||||
height: 40
|
height: 40
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
let baseColor
|
let baseColor;
|
||||||
switch(powerConfirmAction) {
|
switch (powerConfirmAction) {
|
||||||
case "poweroff": baseColor = Theme.error; break
|
case "poweroff":
|
||||||
case "reboot": baseColor = Theme.warning; break
|
baseColor = Theme.error;
|
||||||
default: baseColor = Theme.primary; break
|
break;
|
||||||
|
case "reboot":
|
||||||
|
baseColor = Theme.warning;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
baseColor = Theme.primary;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return confirmButton.containsMouse ?
|
return confirmButton.containsMouse ? Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) : baseColor;
|
||||||
Qt.rgba(baseColor.r, baseColor.g, baseColor.b, 0.9) :
|
|
||||||
baseColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -156,52 +172,49 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: confirmButton
|
id: confirmButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerConfirmVisible = false
|
powerConfirmVisible = false;
|
||||||
executePowerAction(powerConfirmAction)
|
executePowerAction(powerConfirmAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function executePowerAction(action) {
|
|
||||||
console.log("Executing power action:", action)
|
|
||||||
|
|
||||||
let command = []
|
|
||||||
switch(action) {
|
|
||||||
case "logout":
|
|
||||||
command = ["niri", "msg", "action", "quit", "-s"]
|
|
||||||
break
|
|
||||||
case "suspend":
|
|
||||||
command = ["systemctl", "suspend"]
|
|
||||||
break
|
|
||||||
case "reboot":
|
|
||||||
command = ["systemctl", "reboot"]
|
|
||||||
break
|
|
||||||
case "poweroff":
|
|
||||||
command = ["systemctl", "poweroff"]
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.length > 0) {
|
Behavior on opacity {
|
||||||
powerActionProcess.command = command
|
NumberAnimation {
|
||||||
powerActionProcess.running = true
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: powerActionProcess
|
id: powerActionProcess
|
||||||
running: false
|
|
||||||
|
|
||||||
|
running: false
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0)
|
||||||
console.error("Power action failed with exit code:", exitCode)
|
console.error("Power action failed with exit code:", exitCode);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -12,14 +12,11 @@ PanelWindow {
|
|||||||
property bool powerMenuVisible: false
|
property bool powerMenuVisible: false
|
||||||
|
|
||||||
visible: powerMenuVisible
|
visible: powerMenuVisible
|
||||||
|
|
||||||
implicitWidth: 400
|
implicitWidth: 400
|
||||||
implicitHeight: 320
|
implicitHeight: 320
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -33,42 +30,28 @@ PanelWindow {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.min(320, parent.width - Theme.spacingL * 2)
|
width: Math.min(320, parent.width - Theme.spacingL * 2)
|
||||||
height: 320 // Fixed height to prevent cropping
|
height: 320 // Fixed height to prevent cropping
|
||||||
x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL)
|
x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL)
|
||||||
y: Theme.barHeight + Theme.spacingXS
|
y: Theme.barHeight + Theme.spacingXS
|
||||||
color: Theme.popupBackground()
|
color: Theme.popupBackground()
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: powerMenuVisible ? 1 : 0
|
||||||
opacity: powerMenuVisible ? 1.0 : 0.0
|
scale: powerMenuVisible ? 1 : 0.85
|
||||||
scale: powerMenuVisible ? 1.0 : 0.85
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent click-through to background
|
// Prevent click-through to background
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Consume the click to prevent it from reaching the background
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// Consume the click to prevent it from reaching the background
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +72,10 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: parent.width - 150; height: 1 }
|
Item {
|
||||||
|
width: parent.width - 150
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 32
|
width: 32
|
||||||
@@ -107,14 +93,17 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closePowerArea
|
id: closePowerArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power options
|
// Power options
|
||||||
@@ -150,21 +139,24 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: logoutArea
|
id: logoutArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
root.powerConfirmAction = "logout"
|
root.powerConfirmAction = "logout";
|
||||||
root.powerConfirmTitle = "Log Out"
|
root.powerConfirmTitle = "Log Out";
|
||||||
root.powerConfirmMessage = "Are you sure you want to log out?"
|
root.powerConfirmMessage = "Are you sure you want to log out?";
|
||||||
root.powerConfirmVisible = true
|
root.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspend
|
// Suspend
|
||||||
@@ -195,21 +187,24 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: suspendArea
|
id: suspendArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
root.powerConfirmAction = "suspend"
|
root.powerConfirmAction = "suspend";
|
||||||
root.powerConfirmTitle = "Suspend"
|
root.powerConfirmTitle = "Suspend";
|
||||||
root.powerConfirmMessage = "Are you sure you want to suspend the system?"
|
root.powerConfirmMessage = "Are you sure you want to suspend the system?";
|
||||||
root.powerConfirmVisible = true
|
root.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reboot
|
// Reboot
|
||||||
@@ -240,21 +235,24 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: rebootArea
|
id: rebootArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
root.powerConfirmAction = "reboot"
|
root.powerConfirmAction = "reboot";
|
||||||
root.powerConfirmTitle = "Reboot"
|
root.powerConfirmTitle = "Reboot";
|
||||||
root.powerConfirmMessage = "Are you sure you want to reboot the system?"
|
root.powerConfirmMessage = "Are you sure you want to reboot the system?";
|
||||||
root.powerConfirmVisible = true
|
root.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power Off
|
// Power Off
|
||||||
@@ -285,23 +283,46 @@ PanelWindow {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: powerOffArea
|
id: powerOffArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
powerMenuVisible = false
|
powerMenuVisible = false;
|
||||||
root.powerConfirmAction = "poweroff"
|
root.powerConfirmAction = "poweroff";
|
||||||
root.powerConfirmTitle = "Power Off"
|
root.powerConfirmTitle = "Power Off";
|
||||||
root.powerConfirmMessage = "Are you sure you want to power off the system?"
|
root.powerConfirmMessage = "Are you sure you want to power off the system?";
|
||||||
root.powerConfirmVisible = true
|
root.powerConfirmVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
|
import "."
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import "."
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: ramWidget
|
id: ramWidget
|
||||||
@@ -13,19 +13,17 @@ Rectangle {
|
|||||||
width: 55
|
width: 55
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: ramArea.containsMouse ?
|
color: ramArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: ramArea
|
id: ramArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ProcessMonitorService.setSortBy("memory")
|
ProcessMonitorService.setSortBy("memory");
|
||||||
processListDropdown.toggle()
|
processListDropdown.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,14 +33,18 @@ Rectangle {
|
|||||||
|
|
||||||
// RAM icon
|
// RAM icon
|
||||||
Text {
|
Text {
|
||||||
text: "developer_board" // Material Design CPU/processor icon (swapped from CPU widget)
|
text: "developer_board" // Material Design CPU/processor icon (swapped from CPU widget)
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: {
|
color: {
|
||||||
if (SystemMonitorService.memoryUsage > 90) return Theme.error
|
if (SystemMonitorService.memoryUsage > 90)
|
||||||
if (SystemMonitorService.memoryUsage > 75) return Theme.warning
|
return Theme.error;
|
||||||
return Theme.surfaceText
|
|
||||||
|
if (SystemMonitorService.memoryUsage > 75)
|
||||||
|
return Theme.warning;
|
||||||
|
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
@@ -55,5 +57,7 @@ Rectangle {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,32 +2,29 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: settingsPopup
|
id: settingsPopup
|
||||||
|
|
||||||
property bool settingsVisible: false
|
property bool settingsVisible: false
|
||||||
|
|
||||||
signal closingPopup()
|
signal closingPopup()
|
||||||
|
|
||||||
onSettingsVisibleChanged: {
|
onSettingsVisibleChanged: {
|
||||||
if (!settingsVisible) {
|
if (!settingsVisible)
|
||||||
closingPopup()
|
closingPopup();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: settingsVisible
|
visible: settingsVisible
|
||||||
|
|
||||||
implicitWidth: 600
|
implicitWidth: 600
|
||||||
implicitHeight: 700
|
implicitHeight: 700
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -47,11 +44,13 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: settingsPopup.settingsVisible = false
|
onClicked: settingsPopup.settingsVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main settings panel - spotlight-like centered appearance
|
// Main settings panel - spotlight-like centered appearance
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: mainPanel
|
id: mainPanel
|
||||||
|
|
||||||
width: Math.min(600, parent.width - Theme.spacingXL * 2)
|
width: Math.min(600, parent.width - Theme.spacingXL * 2)
|
||||||
height: Math.min(700, parent.height - Theme.spacingXL * 2)
|
height: Math.min(700, parent.height - Theme.spacingXL * 2)
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -59,24 +58,11 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
// Simple opacity and scale control tied directly to settingsVisible
|
// Simple opacity and scale control tied directly to settingsVisible
|
||||||
opacity: settingsPopup.settingsVisible ? 1.0 : 0.0
|
opacity: settingsPopup.settingsVisible ? 1 : 0
|
||||||
scale: settingsPopup.settingsVisible ? 1.0 : 0.95
|
scale: settingsPopup.settingsVisible ? 1 : 0.95
|
||||||
|
// Add shadow effect
|
||||||
Behavior on opacity {
|
layer.enabled: true
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -114,9 +100,7 @@ PanelWindow {
|
|||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: closeButton.containsMouse ?
|
color: closeButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||||
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
|
|
||||||
"transparent"
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "close"
|
text: "close"
|
||||||
@@ -128,12 +112,15 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closeButton
|
id: closeButton
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: settingsPopup.settingsVisible = false
|
onClicked: settingsPopup.settingsVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings sections
|
// Settings sections
|
||||||
@@ -175,11 +162,12 @@ PanelWindow {
|
|||||||
// Circular profile image preview
|
// Circular profile image preview
|
||||||
Item {
|
Item {
|
||||||
id: avatarContainer
|
id: avatarContainer
|
||||||
width: 54
|
|
||||||
height: 54
|
|
||||||
|
|
||||||
property bool hasImage: avatarImageSource.status === Image.Ready
|
property bool hasImage: avatarImageSource.status === Image.Ready
|
||||||
|
|
||||||
|
width: 54
|
||||||
|
height: 54
|
||||||
|
|
||||||
// This rectangle provides the themed ring via its border.
|
// This rectangle provides the themed ring via its border.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -193,12 +181,15 @@ PanelWindow {
|
|||||||
// Hidden Image loader. Its only purpose is to load the texture.
|
// Hidden Image loader. Its only purpose is to load the texture.
|
||||||
Image {
|
Image {
|
||||||
id: avatarImageSource
|
id: avatarImageSource
|
||||||
|
|
||||||
source: {
|
source: {
|
||||||
if (profileImageInput.text === "") return ""
|
if (profileImageInput.text === "")
|
||||||
if (profileImageInput.text.startsWith("/")) {
|
return "";
|
||||||
return "file://" + profileImageInput.text
|
|
||||||
}
|
if (profileImageInput.text.startsWith("/"))
|
||||||
return profileImageInput.text
|
return "file://" + profileImageInput.text;
|
||||||
|
|
||||||
|
return profileImageInput.text;
|
||||||
}
|
}
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
@@ -215,11 +206,12 @@ PanelWindow {
|
|||||||
maskSource: settingsCircularMask
|
maskSource: settingsCircularMask
|
||||||
visible: avatarContainer.hasImage
|
visible: avatarContainer.hasImage
|
||||||
maskThresholdMin: 0.5
|
maskThresholdMin: 0.5
|
||||||
maskSpreadAtMin: 1.0
|
maskSpreadAtMin: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: settingsCircularMask
|
id: settingsCircularMask
|
||||||
|
|
||||||
width: 54 - 10
|
width: 54 - 10
|
||||||
height: 54 - 10
|
height: 54 - 10
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
@@ -232,6 +224,7 @@ PanelWindow {
|
|||||||
color: "black"
|
color: "black"
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for when there is no image.
|
// Fallback for when there is no image.
|
||||||
@@ -248,6 +241,7 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.iconSize + 8
|
font.pixelSize: Theme.iconSize + 8
|
||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error icon for when the image fails to load.
|
// Error icon for when the image fails to load.
|
||||||
@@ -259,6 +253,7 @@ PanelWindow {
|
|||||||
color: Theme.primaryText
|
color: Theme.primaryText
|
||||||
visible: profileImageInput.text !== "" && avatarImageSource.status === Image.Error
|
visible: profileImageInput.text !== "" && avatarImageSource.status === Image.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input field
|
// Input field
|
||||||
@@ -276,6 +271,7 @@ PanelWindow {
|
|||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: profileImageInput
|
id: profileImageInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
@@ -283,9 +279,8 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
text: Prefs.profileImage
|
text: Prefs.profileImage
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
Prefs.setProfileImage(text)
|
Prefs.setProfileImage(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder text
|
// Placeholder text
|
||||||
@@ -303,7 +298,9 @@ PanelWindow {
|
|||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -313,10 +310,15 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clock Settings
|
// Clock Settings
|
||||||
@@ -332,9 +334,13 @@ PanelWindow {
|
|||||||
text: "24-Hour Format"
|
text: "24-Hour Format"
|
||||||
description: "Use 24-hour time format instead of 12-hour AM/PM"
|
description: "Use 24-hour time format instead of 12-hour AM/PM"
|
||||||
checked: Prefs.use24HourClock
|
checked: Prefs.use24HourClock
|
||||||
onToggled: (checked) => Prefs.setClockFormat(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setClockFormat(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather Settings
|
// Weather Settings
|
||||||
@@ -350,7 +356,9 @@ PanelWindow {
|
|||||||
text: "Fahrenheit"
|
text: "Fahrenheit"
|
||||||
description: "Use Fahrenheit instead of Celsius for temperature"
|
description: "Use Fahrenheit instead of Celsius for temperature"
|
||||||
checked: Prefs.useFahrenheit
|
checked: Prefs.useFahrenheit
|
||||||
onToggled: (checked) => Prefs.setTemperatureUnit(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setTemperatureUnit(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weather Location Setting
|
// Weather Location Setting
|
||||||
@@ -375,6 +383,7 @@ PanelWindow {
|
|||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: weatherLocationInput
|
id: weatherLocationInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
@@ -382,9 +391,8 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
text: Prefs.weatherLocationOverride
|
text: Prefs.weatherLocationOverride
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
Prefs.setWeatherLocationOverride(text)
|
Prefs.setWeatherLocationOverride(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder text
|
// Placeholder text
|
||||||
@@ -402,7 +410,9 @@ PanelWindow {
|
|||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -412,8 +422,11 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget Visibility Settings
|
// Widget Visibility Settings
|
||||||
@@ -429,44 +442,58 @@ PanelWindow {
|
|||||||
text: "Focused Window"
|
text: "Focused Window"
|
||||||
description: "Show the currently focused application in the top bar"
|
description: "Show the currently focused application in the top bar"
|
||||||
checked: Prefs.showFocusedWindow
|
checked: Prefs.showFocusedWindow
|
||||||
onToggled: (checked) => Prefs.setShowFocusedWindow(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowFocusedWindow(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggle {
|
SettingsToggle {
|
||||||
text: "Weather Widget"
|
text: "Weather Widget"
|
||||||
description: "Display weather information in the top bar"
|
description: "Display weather information in the top bar"
|
||||||
checked: Prefs.showWeather
|
checked: Prefs.showWeather
|
||||||
onToggled: (checked) => Prefs.setShowWeather(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowWeather(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggle {
|
SettingsToggle {
|
||||||
text: "Media Controls"
|
text: "Media Controls"
|
||||||
description: "Show currently playing media in the top bar"
|
description: "Show currently playing media in the top bar"
|
||||||
checked: Prefs.showMusic
|
checked: Prefs.showMusic
|
||||||
onToggled: (checked) => Prefs.setShowMusic(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowMusic(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggle {
|
SettingsToggle {
|
||||||
text: "Clipboard Button"
|
text: "Clipboard Button"
|
||||||
description: "Show clipboard access button in the top bar"
|
description: "Show clipboard access button in the top bar"
|
||||||
checked: Prefs.showClipboard
|
checked: Prefs.showClipboard
|
||||||
onToggled: (checked) => Prefs.setShowClipboard(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowClipboard(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggle {
|
SettingsToggle {
|
||||||
text: "System Resources"
|
text: "System Resources"
|
||||||
description: "Display CPU and RAM usage indicators"
|
description: "Display CPU and RAM usage indicators"
|
||||||
checked: Prefs.showSystemResources
|
checked: Prefs.showSystemResources
|
||||||
onToggled: (checked) => Prefs.setShowSystemResources(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowSystemResources(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggle {
|
SettingsToggle {
|
||||||
text: "System Tray"
|
text: "System Tray"
|
||||||
description: "Show system tray icons in the top bar"
|
description: "Show system tray icons in the top bar"
|
||||||
checked: Prefs.showSystemTray
|
checked: Prefs.showSystemTray
|
||||||
onToggled: (checked) => Prefs.setShowSystemTray(checked)
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setShowSystemTray(checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display Settings
|
// Display Settings
|
||||||
@@ -483,12 +510,11 @@ PanelWindow {
|
|||||||
description: "Apply warm color temperature to reduce eye strain"
|
description: "Apply warm color temperature to reduce eye strain"
|
||||||
checked: Prefs.nightModeEnabled
|
checked: Prefs.nightModeEnabled
|
||||||
onToggled: (checked) => {
|
onToggled: (checked) => {
|
||||||
Prefs.setNightModeEnabled(checked)
|
Prefs.setNightModeEnabled(checked);
|
||||||
if (checked) {
|
if (checked)
|
||||||
nightModeEnableProcess.running = true
|
nightModeEnableProcess.running = true;
|
||||||
} else {
|
else
|
||||||
nightModeDisableProcess.running = true
|
nightModeDisableProcess.running = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,8 +523,8 @@ PanelWindow {
|
|||||||
description: "Use light theme instead of dark theme"
|
description: "Use light theme instead of dark theme"
|
||||||
checked: Prefs.isLightMode
|
checked: Prefs.isLightMode
|
||||||
onToggled: (checked) => {
|
onToggled: (checked) => {
|
||||||
Prefs.setLightMode(checked)
|
Prefs.setLightMode(checked);
|
||||||
Theme.isLightMode = checked
|
Theme.isLightMode = checked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,10 +549,9 @@ PanelWindow {
|
|||||||
rightIcon: "circle"
|
rightIcon: "circle"
|
||||||
unit: "%"
|
unit: "%"
|
||||||
showValue: true
|
showValue: true
|
||||||
|
|
||||||
onSliderDragFinished: (finalValue) => {
|
onSliderDragFinished: (finalValue) => {
|
||||||
let transparencyValue = finalValue / 100.0
|
let transparencyValue = finalValue / 100;
|
||||||
Prefs.setTopBarTransparency(transparencyValue)
|
Prefs.setTopBarTransparency(transparencyValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,6 +562,7 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popup Transparency
|
// Popup Transparency
|
||||||
@@ -560,10 +586,9 @@ PanelWindow {
|
|||||||
rightIcon: "circle"
|
rightIcon: "circle"
|
||||||
unit: "%"
|
unit: "%"
|
||||||
showValue: true
|
showValue: true
|
||||||
|
|
||||||
onSliderDragFinished: (finalValue) => {
|
onSliderDragFinished: (finalValue) => {
|
||||||
let transparencyValue = finalValue / 100.0
|
let transparencyValue = finalValue / 100;
|
||||||
Prefs.setPopupTransparency(transparencyValue)
|
Prefs.setPopupTransparency(transparencyValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,6 +599,7 @@ PanelWindow {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme Picker
|
// Theme Picker
|
||||||
@@ -591,48 +617,69 @@ PanelWindow {
|
|||||||
ThemePicker {
|
ThemePicker {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add shadow effect
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: MultiEffect {
|
layer.effect: MultiEffect {
|
||||||
shadowEnabled: true
|
shadowEnabled: true
|
||||||
shadowHorizontalOffset: 0
|
shadowHorizontalOffset: 0
|
||||||
shadowVerticalOffset: 8
|
shadowVerticalOffset: 8
|
||||||
shadowBlur: 1.0
|
shadowBlur: 1
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||||
shadowOpacity: 0.3
|
shadowOpacity: 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Night mode processes
|
// Night mode processes
|
||||||
Process {
|
Process {
|
||||||
id: nightModeEnableProcess
|
id: nightModeEnableProcess
|
||||||
|
|
||||||
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Failed to enable night mode")
|
console.warn("Failed to enable night mode");
|
||||||
Prefs.setNightModeEnabled(false)
|
Prefs.setNightModeEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: nightModeDisableProcess
|
id: nightModeDisableProcess
|
||||||
|
|
||||||
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
|
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0)
|
||||||
console.warn("Failed to disable night mode")
|
console.warn("Failed to disable night mode");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,7 +687,7 @@ PanelWindow {
|
|||||||
FocusScope {
|
FocusScope {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: settingsPopup.settingsVisible
|
focus: settingsPopup.settingsVisible
|
||||||
|
|
||||||
Keys.onEscapePressed: settingsPopup.settingsVisible = false
|
Keys.onEscapePressed: settingsPopup.settingsVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ Column {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divider
|
// Divider
|
||||||
@@ -43,6 +44,8 @@ Column {
|
|||||||
// Content
|
// Content
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,16 +13,7 @@ Rectangle {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 60
|
height: 60
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: toggleArea.containsMouse ?
|
color: toggleArea.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)
|
||||||
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)
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -51,30 +42,26 @@ Rectangle {
|
|||||||
width: Math.min(implicitWidth, root.width - 120)
|
width: Math.min(implicitWidth, root.width - 120)
|
||||||
visible: root.description.length > 0
|
visible: root.description.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle switch
|
// Toggle switch
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: toggle
|
id: toggle
|
||||||
|
|
||||||
width: 48
|
width: 48
|
||||||
height: 24
|
height: 24
|
||||||
radius: 12
|
radius: 12
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
color: root.checked ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
color: root.checked ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: toggleHandle
|
id: toggleHandle
|
||||||
|
|
||||||
width: 20
|
width: 20
|
||||||
height: 20
|
height: 20
|
||||||
radius: 10
|
radius: 10
|
||||||
@@ -87,6 +74,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -94,19 +82,39 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: toggleArea
|
id: toggleArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.checked = !root.checked
|
root.checked = !root.checked;
|
||||||
root.toggled(root.checked)
|
root.toggled(root.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import qs.Services
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: themePicker
|
id: themePicker
|
||||||
|
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -17,22 +18,11 @@ Column {
|
|||||||
// Theme description
|
// Theme description
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (Theme.isDynamicTheme) {
|
if (Theme.isDynamicTheme)
|
||||||
return "Wallpaper-based dynamic colors"
|
return "Wallpaper-based dynamic colors";
|
||||||
}
|
|
||||||
var descriptions = [
|
var descriptions = ["Material blue inspired by modern interfaces", "Deep blue inspired by material 3", "Rich purple tones for BB elegance", "Natural green for productivity", "Energetic orange for creativity", "Bold red for impact", "Cool cyan for tranquility", "Vibrant pink for expression", "Warm amber for comfort", "Soft coral for gentle warmth"];
|
||||||
"Material blue inspired by modern interfaces",
|
return descriptions[Theme.currentThemeIndex] || "Select a theme";
|
||||||
"Deep blue inspired by material 3",
|
|
||||||
"Rich purple tones for BB elegance",
|
|
||||||
"Natural green for productivity",
|
|
||||||
"Energetic orange for creativity",
|
|
||||||
"Bold red for impact",
|
|
||||||
"Cool cyan for tranquility",
|
|
||||||
"Vibrant pink for expression",
|
|
||||||
"Warm amber for comfort",
|
|
||||||
"Soft coral for gentle warmth"
|
|
||||||
]
|
|
||||||
return descriptions[Theme.currentThemeIndex] || "Select a theme"
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
@@ -62,22 +52,7 @@ Column {
|
|||||||
color: Theme.themes[index].primary
|
color: Theme.themes[index].primary
|
||||||
border.color: Theme.outline
|
border.color: Theme.outline
|
||||||
border.width: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 2 : 1
|
border.width: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 2 : 1
|
||||||
|
scale: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 1.1 : 1
|
||||||
scale: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 1.1 : 1.0
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Theme name tooltip
|
// Theme name tooltip
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -94,24 +69,46 @@ Column {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: nameText
|
id: nameText
|
||||||
|
|
||||||
text: Theme.themes[index].name
|
text: Theme.themes[index].name
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Theme.switchTheme(index, false)
|
Theme.switchTheme(index, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second row - Red, Cyan, Pink, Amber, Coral
|
// Second row - Red, Cyan, Pink, Amber, Coral
|
||||||
@@ -124,6 +121,7 @@ Column {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property int themeIndex: index + 5
|
property int themeIndex: index + 5
|
||||||
|
|
||||||
width: 32
|
width: 32
|
||||||
height: 32
|
height: 32
|
||||||
radius: 16
|
radius: 16
|
||||||
@@ -131,22 +129,7 @@ Column {
|
|||||||
border.color: Theme.outline
|
border.color: Theme.outline
|
||||||
border.width: Theme.currentThemeIndex === themeIndex ? 2 : 1
|
border.width: Theme.currentThemeIndex === themeIndex ? 2 : 1
|
||||||
visible: themeIndex < Theme.themes.length
|
visible: themeIndex < Theme.themes.length
|
||||||
|
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1
|
||||||
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1.0
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Theme name tooltip
|
// Theme name tooltip
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -163,26 +146,48 @@ Column {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: nameText2
|
id: nameText2
|
||||||
|
|
||||||
text: themeIndex < Theme.themes.length ? Theme.themes[themeIndex].name : ""
|
text: themeIndex < Theme.themes.length ? Theme.themes[themeIndex].name : ""
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea2
|
id: mouseArea2
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (themeIndex < Theme.themes.length) {
|
if (themeIndex < Theme.themes.length)
|
||||||
Theme.switchTheme(themeIndex)
|
Theme.switchTheme(themeIndex);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacer for better visual separation
|
// Spacer for better visual separation
|
||||||
@@ -197,26 +202,22 @@ Column {
|
|||||||
height: 40
|
height: 40
|
||||||
radius: 20
|
radius: 20
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") {
|
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
||||||
} else {
|
else
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
border.color: {
|
border.color: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") {
|
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5)
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5);
|
||||||
} else if (Theme.isDynamicTheme) {
|
else if (Theme.isDynamicTheme)
|
||||||
return Theme.primary
|
return Theme.primary;
|
||||||
} else {
|
else
|
||||||
return Theme.outline
|
return Theme.outline;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
border.width: Theme.isDynamicTheme ? 2 : 1
|
border.width: Theme.isDynamicTheme ? 2 : 1
|
||||||
|
scale: Theme.isDynamicTheme ? 1.1 : (autoMouseArea.containsMouse ? 1.02 : 1)
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -224,14 +225,18 @@ Column {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return "error"
|
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
else return "palette"
|
return "error";
|
||||||
|
else
|
||||||
|
return "palette";
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
color: {
|
color: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return Theme.error
|
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
else return Theme.surfaceText
|
return Theme.error;
|
||||||
|
else
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -239,51 +244,34 @@ Column {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error") return "Error"
|
if (ToastService.wallpaperErrorStatus === "error")
|
||||||
else if (ToastService.wallpaperErrorStatus === "matugen_missing") return "No matugen"
|
return "Error";
|
||||||
else return "Auto"
|
else if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
|
return "No matugen";
|
||||||
|
else
|
||||||
|
return "Auto";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: {
|
color: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return Theme.error
|
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
else return Theme.surfaceText
|
return Theme.error;
|
||||||
|
else
|
||||||
|
return Theme.surfaceText;
|
||||||
}
|
}
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scale: Theme.isDynamicTheme ? 1.1 : (autoMouseArea.containsMouse ? 1.02 : 1.0)
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: autoMouseArea
|
id: autoMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Theme.switchTheme(10, true)
|
Theme.switchTheme(10, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,14 +290,14 @@ Column {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: autoTooltipText
|
id: autoTooltipText
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
if (ToastService.wallpaperErrorStatus === "error") {
|
if (ToastService.wallpaperErrorStatus === "error")
|
||||||
return "Wallpaper symlink missing at ~/quickshell/current_wallpaper"
|
return "Wallpaper symlink missing at ~/quickshell/current_wallpaper";
|
||||||
} else if (ToastService.wallpaperErrorStatus === "matugen_missing") {
|
else if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||||
return "Install matugen package for dynamic themes"
|
return "Install matugen package for dynamic themes";
|
||||||
} else {
|
else
|
||||||
return "Dynamic wallpaper-based colors"
|
return "Dynamic wallpaper-based colors";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceText
|
color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceText
|
||||||
@@ -318,7 +306,35 @@ Column {
|
|||||||
width: Math.min(implicitWidth, 250)
|
width: Math.min(implicitWidth, 250)
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -10,11 +10,9 @@ PanelWindow {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
visible: ToastService.toastVisible
|
visible: ToastService.toastVisible
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -24,80 +22,47 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes the background transparent to mouse events
|
|
||||||
mask: Region {
|
|
||||||
item: toast
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: toast
|
id: toast
|
||||||
|
|
||||||
width: Math.min(400, Screen.width - Theme.spacingL * 2)
|
width: Math.min(400, Screen.width - Theme.spacingL * 2)
|
||||||
height: toastContent.height + Theme.spacingL * 2
|
height: toastContent.height + Theme.spacingL * 2
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
y: Theme.barHeight + Theme.spacingL
|
y: Theme.barHeight + Theme.spacingL
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
switch (ToastService.currentLevel) {
|
switch (ToastService.currentLevel) {
|
||||||
case ToastService.levelError: return Theme.error
|
case ToastService.levelError:
|
||||||
case ToastService.levelWarn: return Theme.warning
|
return Theme.error;
|
||||||
case ToastService.levelInfo: return Theme.primary
|
case ToastService.levelWarn:
|
||||||
default: return Theme.primary
|
return Theme.warning;
|
||||||
|
case ToastService.levelInfo:
|
||||||
|
return Theme.primary;
|
||||||
|
default:
|
||||||
|
return Theme.primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: MultiEffect {
|
opacity: ToastService.toastVisible ? 0.9 : 0
|
||||||
shadowEnabled: true
|
scale: ToastService.toastVisible ? 1 : 0.9
|
||||||
shadowHorizontalOffset: 0
|
|
||||||
shadowVerticalOffset: 4
|
|
||||||
shadowBlur: 0.8
|
|
||||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
|
||||||
shadowOpacity: 0.3
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: ToastService.toastVisible ? 0.9 : 0.0
|
|
||||||
scale: ToastService.toastVisible ? 1.0 : 0.9
|
|
||||||
|
|
||||||
transform: Translate {
|
|
||||||
y: ToastService.toastVisible ? 0 : -20
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: toastContent
|
id: toastContent
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
switch (ToastService.currentLevel) {
|
switch (ToastService.currentLevel) {
|
||||||
case ToastService.levelError: return "error"
|
case ToastService.levelError:
|
||||||
case ToastService.levelWarn: return "warning"
|
return "error";
|
||||||
case ToastService.levelInfo: return "info"
|
case ToastService.levelWarn:
|
||||||
default: return "info"
|
return "warning";
|
||||||
|
case ToastService.levelInfo:
|
||||||
|
return "info";
|
||||||
|
default:
|
||||||
|
return "info";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
@@ -115,11 +80,56 @@ PanelWindow {
|
|||||||
width: Math.min(implicitWidth, 300)
|
width: Math.min(implicitWidth, 300)
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: ToastService.hideToast()
|
onClicked: ToastService.hideToast()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 4
|
||||||
|
shadowBlur: 0.8
|
||||||
|
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||||
|
shadowOpacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
y: ToastService.toastVisible ? 0 : -20
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Makes the background transparent to mouse events
|
||||||
|
mask: Region {
|
||||||
|
item: toast
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,8 @@ import qs.Services
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property list<real> audioLevels: [0, 0, 0, 0]
|
property var audioLevels: [0, 0, 0, 0]
|
||||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
readonly property bool hasActiveMedia: activePlayer !== null
|
readonly property bool hasActiveMedia: activePlayer !== null
|
||||||
property bool cavaAvailable: false
|
property bool cavaAvailable: false
|
||||||
|
|
||||||
@@ -18,56 +18,62 @@ Item {
|
|||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: cavaCheck
|
id: cavaCheck
|
||||||
|
|
||||||
command: ["which", "cava"]
|
command: ["which", "cava"]
|
||||||
running: true
|
running: true
|
||||||
onExited: (exitCode) => {
|
onExited: (exitCode) => {
|
||||||
root.cavaAvailable = exitCode === 0
|
root.cavaAvailable = exitCode === 0;
|
||||||
if (root.cavaAvailable) {
|
if (root.cavaAvailable) {
|
||||||
console.log("cava found - enabling real audio visualization")
|
console.log("cava found - enabling real audio visualization");
|
||||||
cavaProcess.running = Qt.binding(() => root.hasActiveMedia && root.activePlayer?.playbackState === MprisPlaybackState.Playing)
|
cavaProcess.running = Qt.binding(() => {
|
||||||
|
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("cava not found - using fallback animation")
|
console.log("cava not found - using fallback animation");
|
||||||
fallbackTimer.running = Qt.binding(() => root.hasActiveMedia && root.activePlayer?.playbackState === MprisPlaybackState.Playing)
|
fallbackTimer.running = Qt.binding(() => {
|
||||||
|
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: cavaProcess
|
id: cavaProcess
|
||||||
|
|
||||||
running: false
|
running: false
|
||||||
command: ["sh", "-c", `printf '[general]\nmode=normal\nframerate=30\nautosens=0\nsensitivity=50\nbars=4\n[output]\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nchannels=mono\nmono_option=average\n[smoothing]\nnoise_reduction=20' | cava -p /dev/stdin`]
|
command: ["sh", "-c", `printf '[general]\nmode=normal\nframerate=30\nautosens=0\nsensitivity=50\nbars=4\n[output]\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nchannels=mono\nmono_option=average\n[smoothing]\nnoise_reduction=20' | cava -p /dev/stdin`]
|
||||||
|
onRunningChanged: {
|
||||||
|
if (!running)
|
||||||
|
root.audioLevels = [0, 0, 0, 0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
splitMarker: "\n"
|
splitMarker: "\n"
|
||||||
onRead: data => {
|
onRead: (data) => {
|
||||||
if (data.trim()) {
|
if (data.trim()) {
|
||||||
let points = data.split(";").map(p => parseFloat(p.trim())).filter(p => !isNaN(p))
|
let points = data.split(";").map((p) => {
|
||||||
if (points.length >= 4) {
|
return parseFloat(p.trim());
|
||||||
root.audioLevels = [points[0], points[1], points[2], points[3]]
|
}).filter((p) => {
|
||||||
}
|
return !isNaN(p);
|
||||||
|
});
|
||||||
|
if (points.length >= 4)
|
||||||
|
root.audioLevels = [points[0], points[1], points[2], points[3]];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (!running) {
|
|
||||||
root.audioLevels = [0, 0, 0, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: fallbackTimer
|
id: fallbackTimer
|
||||||
|
|
||||||
running: false
|
running: false
|
||||||
interval: 100
|
interval: 100
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.audioLevels = [
|
root.audioLevels = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20];
|
||||||
Math.random() * 40 + 10,
|
|
||||||
Math.random() * 60 + 20,
|
|
||||||
Math.random() * 50 + 15,
|
|
||||||
Math.random() * 35 + 20
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,14 +87,14 @@ Item {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
width: 3
|
width: 3
|
||||||
height: {
|
height: {
|
||||||
if (root.activePlayer?.playbackState === MprisPlaybackState.Playing && root.audioLevels.length > index) {
|
if (root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing && root.audioLevels.length > index) {
|
||||||
const rawLevel = root.audioLevels[index] || 0
|
const rawLevel = root.audioLevels[index] || 0;
|
||||||
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100
|
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100;
|
||||||
const maxHeight = Theme.iconSize - 2
|
const maxHeight = Theme.iconSize - 2;
|
||||||
const minHeight = 3
|
const minHeight = 3;
|
||||||
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight)
|
return minHeight + (scaledLevel / 100) * (maxHeight - minHeight);
|
||||||
}
|
}
|
||||||
return 3
|
return 3;
|
||||||
}
|
}
|
||||||
radius: 1.5
|
radius: 1.5
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
@@ -99,8 +105,13 @@ Item {
|
|||||||
duration: 80
|
duration: 80
|
||||||
easing.type: Easing.OutQuad
|
easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,19 +12,14 @@ Rectangle {
|
|||||||
width: clockRow.implicitWidth + Theme.spacingS * 2
|
width: clockRow.implicitWidth + Theme.spacingS * 2
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: clockMouseArea.containsMouse ?
|
color: clockMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
Component.onCompleted: {
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
root.currentDate = systemClock.date;
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: clockRow
|
id: clockRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
@@ -50,26 +45,33 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemClock {
|
SystemClock {
|
||||||
id: systemClock
|
id: systemClock
|
||||||
|
|
||||||
precision: SystemClock.Seconds
|
precision: SystemClock.Seconds
|
||||||
onDateChanged: root.currentDate = systemClock.date
|
onDateChanged: root.currentDate = systemClock.date
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
root.currentDate = systemClock.date
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: clockMouseArea
|
id: clockMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clockClicked()
|
root.clockClicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,29 +12,35 @@ Rectangle {
|
|||||||
width: Math.max(80, controlIndicators.implicitWidth + Theme.spacingS * 2)
|
width: Math.max(80, controlIndicators.implicitWidth + Theme.spacingS * 2)
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: controlCenterArea.containsMouse || root.isActive ?
|
color: controlCenterArea.containsMouse || root.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: controlIndicators
|
id: controlIndicators
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
// Network Status Icon
|
// Network Status Icon
|
||||||
Text {
|
Text {
|
||||||
text: {
|
text: {
|
||||||
if (NetworkService.networkStatus === "ethernet") return "lan"
|
if (NetworkService.networkStatus === "ethernet") {
|
||||||
else if (NetworkService.networkStatus === "wifi") {
|
return "lan";
|
||||||
|
} else if (NetworkService.networkStatus === "wifi") {
|
||||||
switch (WifiService.wifiSignalStrength) {
|
switch (WifiService.wifiSignalStrength) {
|
||||||
case "excellent": return "wifi"
|
case "excellent":
|
||||||
case "good": return "wifi_2_bar"
|
return "wifi";
|
||||||
case "fair": return "wifi_1_bar"
|
case "good":
|
||||||
case "poor": return "wifi_calling_3"
|
return "wifi_2_bar";
|
||||||
default: return "wifi"
|
case "fair":
|
||||||
|
return "wifi_1_bar";
|
||||||
|
case "poor":
|
||||||
|
return "wifi_calling_3";
|
||||||
|
default:
|
||||||
|
return "wifi";
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return "wifi_off";
|
||||||
}
|
}
|
||||||
else return "wifi_off"
|
|
||||||
}
|
}
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
@@ -64,39 +70,37 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: audioIcon
|
id: audioIcon
|
||||||
text: AudioService.sinkMuted ? "volume_off" :
|
|
||||||
AudioService.volumeLevel < 33 ? "volume_down" : "volume_up"
|
text: AudioService.sinkMuted ? "volume_off" : AudioService.volumeLevel < 33 ? "volume_down" : "volume_up"
|
||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 8
|
font.pixelSize: Theme.iconSize - 8
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: audioWheelArea.containsMouse || controlCenterArea.containsMouse || root.isActive ?
|
color: audioWheelArea.containsMouse || controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
Theme.primary : Theme.surfaceText
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Scroll up - increase volume
|
||||||
|
// Scroll down - decrease volume
|
||||||
|
|
||||||
id: audioWheelArea
|
id: audioWheelArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
onWheel: function(wheelEvent) {
|
onWheel: function(wheelEvent) {
|
||||||
let delta = wheelEvent.angleDelta.y
|
let delta = wheelEvent.angleDelta.y;
|
||||||
let currentVolume = AudioService.volumeLevel
|
let currentVolume = AudioService.volumeLevel;
|
||||||
let newVolume
|
let newVolume;
|
||||||
|
if (delta > 0)
|
||||||
if (delta > 0) {
|
newVolume = Math.min(100, currentVolume + 5);
|
||||||
// Scroll up - increase volume
|
else
|
||||||
newVolume = Math.min(100, currentVolume + 5)
|
newVolume = Math.max(0, currentVolume - 5);
|
||||||
} else {
|
AudioService.setVolume(newVolume);
|
||||||
// Scroll down - decrease volume
|
wheelEvent.accepted = true;
|
||||||
newVolume = Math.max(0, currentVolume - 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioService.setVolume(newVolume)
|
|
||||||
wheelEvent.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Microphone Icon (when active)
|
// Microphone Icon (when active)
|
||||||
@@ -109,16 +113,17 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: false // TODO: Add mic detection
|
visible: false // TODO: Add mic detection
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: controlCenterArea
|
id: controlCenterArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked()
|
root.clicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,5 +132,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,33 +8,24 @@ Rectangle {
|
|||||||
width: Math.max(contentRow.implicitWidth + Theme.spacingS * 2, 60)
|
width: Math.max(contentRow.implicitWidth + Theme.spacingS * 2, 60)
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: mouseArea.containsMouse ?
|
color: mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
visible: FocusedWindowService.niriAvailable && (FocusedWindowService.focusedAppName || FocusedWindowService.focusedWindowTitle)
|
visible: FocusedWindowService.niriAvailable && (FocusedWindowService.focusedAppName || FocusedWindowService.focusedWindowTitle)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: contentRow
|
id: contentRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: appText
|
id: appText
|
||||||
|
|
||||||
text: FocusedWindowService.focusedAppName || ""
|
text: FocusedWindowService.focusedAppName || ""
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// Limit app name width
|
// Limit app name width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
@@ -51,25 +42,35 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: titleText
|
id: titleText
|
||||||
|
|
||||||
text: FocusedWindowService.focusedWindowTitle || ""
|
text: FocusedWindowService.focusedWindowTitle || ""
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// Limit title width - increased for longer titles
|
// Limit title width - increased for longer titles
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
width: Math.min(implicitWidth, 350)
|
width: Math.min(implicitWidth, 350)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Non-interactive widget - just provides hover state for visual feedback
|
||||||
|
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
// Non-interactive widget - just provides hover state for visual feedback
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smooth width animation when the text changes
|
// Smooth width animation when the text changes
|
||||||
@@ -78,5 +79,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -21,12 +21,12 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: launcherArea
|
id: launcherArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
LauncherService.toggleAppLauncher()
|
LauncherService.toggleAppLauncher();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,5 +35,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,37 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "shown"
|
||||||
|
to: "hidden"
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 500
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "opacity,width"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: "hidden"
|
||||||
|
to: "shown"
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "opacity,width"
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: mediaRow
|
id: mediaRow
|
||||||
@@ -64,9 +95,8 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 140
|
width: 140
|
||||||
text: {
|
text: {
|
||||||
if (!activePlayer || !activePlayer.trackTitle) {
|
if (!activePlayer || !activePlayer.trackTitle)
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
|
|
||||||
let identity = activePlayer.identity || "";
|
let identity = activePlayer.identity || "";
|
||||||
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
|
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari");
|
||||||
@@ -209,38 +239,6 @@ Rectangle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions: [
|
|
||||||
Transition {
|
|
||||||
from: "shown"
|
|
||||||
to: "hidden"
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
PauseAnimation {
|
|
||||||
duration: 500
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
properties: "opacity,width"
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
Transition {
|
|
||||||
from: "hidden"
|
|
||||||
to: "shown"
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
properties: "opacity,width"
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ Rectangle {
|
|||||||
|
|
||||||
property bool hasUnread: false
|
property bool hasUnread: false
|
||||||
property bool isActive: false
|
property bool isActive: false
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
width: 40
|
width: 40
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: notificationArea.containsMouse || root.isActive ?
|
color: notificationArea.containsMouse || root.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
|
||||||
Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -21,8 +20,7 @@ Rectangle {
|
|||||||
font.family: Theme.iconFont
|
font.family: Theme.iconFont
|
||||||
font.pixelSize: Theme.iconSize - 6
|
font.pixelSize: Theme.iconSize - 6
|
||||||
font.weight: Theme.iconFontWeight
|
font.weight: Theme.iconFontWeight
|
||||||
color: notificationArea.containsMouse || root.isActive ?
|
color: notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||||
Theme.primary : Theme.surfaceText
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification dot indicator
|
// Notification dot indicator
|
||||||
@@ -40,12 +38,12 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: notificationArea
|
id: notificationArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked()
|
root.clicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,5 +52,7 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,25 +15,27 @@ Rectangle {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: systemTrayRow
|
id: systemTrayRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: SystemTray.items
|
model: SystemTray.items
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
|
property var trayItem: modelData
|
||||||
|
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
radius: Theme.cornerRadiusSmall
|
radius: Theme.cornerRadiusSmall
|
||||||
color: trayItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: trayItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
property var trayItem: modelData
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: 18
|
width: 18
|
||||||
height: 18
|
height: 18
|
||||||
source: {
|
source: {
|
||||||
let icon = trayItem?.icon;
|
let icon = trayItem && trayItem.icon;
|
||||||
if (typeof icon === 'string' || icon instanceof String) {
|
if (typeof icon === 'string' || icon instanceof String) {
|
||||||
if (icon.includes("?path=")) {
|
if (icon.includes("?path=")) {
|
||||||
const [name, path] = icon.split("?path=");
|
const [name, path] = icon.split("?path=");
|
||||||
@@ -51,22 +53,23 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: trayItemArea
|
id: trayItemArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: (mouse) => {
|
onClicked: (mouse) => {
|
||||||
if (!trayItem) return;
|
if (!trayItem)
|
||||||
|
return ;
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
if (!trayItem.onlyMenu) {
|
if (!trayItem.onlyMenu)
|
||||||
trayItem.activate()
|
trayItem.activate();
|
||||||
}
|
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
if (trayItem.hasMenu) {
|
if (trayItem.hasMenu)
|
||||||
customTrayMenu.showMenu(mouse.x, mouse.y)
|
customTrayMenu.showMenu(mouse.x, mouse.y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,13 +80,14 @@ Rectangle {
|
|||||||
property bool menuVisible: false
|
property bool menuVisible: false
|
||||||
|
|
||||||
function showMenu(x, y) {
|
function showMenu(x, y) {
|
||||||
root.menuRequested(customTrayMenu, trayItem, x, y)
|
root.menuRequested(customTrayMenu, trayItem, x, y);
|
||||||
menuVisible = true
|
menuVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideMenu() {
|
function hideMenu() {
|
||||||
menuVisible = false
|
menuVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -91,8 +95,13 @@ Rectangle {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,61 +1,54 @@
|
|||||||
|
import "../../Common/Utilities.js" as Utils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.SystemTray
|
|
||||||
import Quickshell.Services.Notifications
|
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import "../../Common/Utilities.js" as Utils
|
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
|
// Proxy objects for external connections
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var modelData
|
property var modelData
|
||||||
screen: modelData
|
|
||||||
property string screenName: modelData.name
|
property string screenName: modelData.name
|
||||||
|
|
||||||
// Transparency property for the top bar background
|
// Transparency property for the top bar background
|
||||||
property real backgroundTransparency: Prefs.topBarTransparency
|
property real backgroundTransparency: Prefs.topBarTransparency
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Prefs
|
|
||||||
function onTopBarTransparencyChanged() {
|
|
||||||
root.backgroundTransparency = Prefs.topBarTransparency
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Notification properties
|
// Notification properties
|
||||||
readonly property int notificationCount: NotificationService.notifications.length
|
readonly property int notificationCount: NotificationService.notifications.length
|
||||||
|
|
||||||
|
screen: modelData
|
||||||
|
implicitHeight: Theme.barHeight - 4
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onTopBarTransparencyChanged() {
|
||||||
|
root.backgroundTransparency = Prefs.topBarTransparency;
|
||||||
|
}
|
||||||
|
|
||||||
|
target: Prefs
|
||||||
|
}
|
||||||
|
|
||||||
// Proxy objects for external connections
|
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: notificationHistory
|
id: notificationHistory
|
||||||
|
|
||||||
property int count: 0
|
property int count: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
right: true
|
right: true
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitHeight: Theme.barHeight - 4
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
// Floating panel container with margins
|
// Floating panel container with margins
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -69,16 +62,7 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Theme.cornerRadiusXLarge
|
radius: Theme.cornerRadiusXLarge
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, root.backgroundTransparency)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, root.backgroundTransparency)
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: 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 {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -96,18 +80,32 @@ PanelWindow {
|
|||||||
SequentialAnimation on opacity {
|
SequentialAnimation on opacity {
|
||||||
running: false
|
running: false
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
to: 0.08
|
to: 0.08
|
||||||
duration: Theme.extraLongDuration
|
duration: Theme.extraLongDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
to: 0.02
|
to: 0.02
|
||||||
duration: Theme.extraLongDuration
|
duration: Theme.extraLongDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -120,6 +118,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: leftSection
|
id: leftSection
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -138,14 +137,15 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: Prefs.showFocusedWindow
|
visible: Prefs.showFocusedWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClockWidget {
|
ClockWidget {
|
||||||
id: clockWidget
|
id: clockWidget
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
onClockClicked: {
|
onClockClicked: {
|
||||||
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
|
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,27 +154,26 @@ PanelWindow {
|
|||||||
anchors.right: clockWidget.left
|
anchors.right: clockWidget.left
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
visible: Prefs.showMusic && MprisController.activePlayer
|
visible: Prefs.showMusic && MprisController.activePlayer
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
|
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WeatherWidget {
|
WeatherWidget {
|
||||||
id: weatherWidget
|
id: weatherWidget
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: clockWidget.right
|
anchors.left: clockWidget.right
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
|
||||||
visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0
|
visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
|
centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: rightSection
|
id: rightSection
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -184,12 +183,12 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: Prefs.showSystemTray
|
visible: Prefs.showSystemTray
|
||||||
onMenuRequested: (menu, item, x, y) => {
|
onMenuRequested: (menu, item, x, y) => {
|
||||||
trayMenuPopup.currentTrayMenu = menu
|
trayMenuPopup.currentTrayMenu = menu;
|
||||||
trayMenuPopup.currentTrayItem = item
|
trayMenuPopup.currentTrayItem = item;
|
||||||
trayMenuPopup.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL
|
trayMenuPopup.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL;
|
||||||
trayMenuPopup.trayMenuY = Theme.barHeight - Theme.spacingXS
|
trayMenuPopup.trayMenuY = Theme.barHeight - Theme.spacingXS;
|
||||||
trayMenuPopup.showTrayMenu = true
|
trayMenuPopup.showTrayMenu = true;
|
||||||
menu.menuVisible = true
|
menu.menuVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,12 +211,12 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: clipboardArea
|
id: clipboardArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
clipboardHistoryPopup.toggle()
|
clipboardHistoryPopup.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +225,9 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// System Monitor Widgets
|
// System Monitor Widgets
|
||||||
@@ -245,7 +246,7 @@ PanelWindow {
|
|||||||
hasUnread: root.notificationCount > 0
|
hasUnread: root.notificationCount > 0
|
||||||
isActive: notificationCenter.notificationHistoryVisible
|
isActive: notificationCenter.notificationHistoryVisible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notificationCenter.notificationHistoryVisible = !notificationCenter.notificationHistoryVisible
|
notificationCenter.notificationHistoryVisible = !notificationCenter.notificationHistoryVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,25 +255,29 @@ PanelWindow {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
batteryPopupVisible: batteryControlPopup.batteryPopupVisible
|
batteryPopupVisible: batteryControlPopup.batteryPopupVisible
|
||||||
onToggleBatteryPopup: {
|
onToggleBatteryPopup: {
|
||||||
batteryControlPopup.batteryPopupVisible = !batteryControlPopup.batteryPopupVisible
|
batteryControlPopup.batteryPopupVisible = !batteryControlPopup.batteryPopupVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlCenterButton {
|
ControlCenterButton {
|
||||||
|
// Bluetooth devices are automatically updated via signals
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
isActive: controlCenterPopup.controlCenterVisible
|
isActive: controlCenterPopup.controlCenterVisible
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
controlCenterPopup.controlCenterVisible = !controlCenterPopup.controlCenterVisible
|
controlCenterPopup.controlCenterVisible = !controlCenterPopup.controlCenterVisible;
|
||||||
if (controlCenterPopup.controlCenterVisible) {
|
if (controlCenterPopup.controlCenterVisible) {
|
||||||
if (NetworkService.wifiEnabled) {
|
if (NetworkService.wifiEnabled)
|
||||||
WifiService.scanWifi()
|
WifiService.scanWifi();
|
||||||
}
|
|
||||||
// Bluetooth devices are automatically updated via signals
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,33 +5,17 @@ import qs.Services
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
// Visibility is now controlled by TopBar.qml
|
// Visibility is now controlled by TopBar.qml
|
||||||
width: visible ? Math.min(100, weatherRow.implicitWidth + Theme.spacingS * 2) : 0
|
width: visible ? Math.min(100, weatherRow.implicitWidth + Theme.spacingS * 2) : 0
|
||||||
height: 30
|
height: 30
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: weatherArea.containsMouse ?
|
color: weatherArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: weatherRow
|
id: weatherRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
@@ -50,14 +34,32 @@ Rectangle {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: weatherArea
|
id: weatherArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: root.clicked()
|
onClicked: root.clicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,41 @@ Rectangle {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
|
property int currentWorkspace: getDisplayActiveWorkspace()
|
||||||
|
property var workspaceList: getDisplayWorkspaces()
|
||||||
|
|
||||||
|
function getDisplayWorkspaces() {
|
||||||
|
if (!NiriWorkspaceService.niriAvailable || NiriWorkspaceService.allWorkspaces.length === 0)
|
||||||
|
return [1, 2];
|
||||||
|
|
||||||
|
if (!root.screenName)
|
||||||
|
return NiriWorkspaceService.getCurrentOutputWorkspaceNumbers();
|
||||||
|
|
||||||
|
var displayWorkspaces = [];
|
||||||
|
for (var i = 0; i < NiriWorkspaceService.allWorkspaces.length; i++) {
|
||||||
|
var ws = NiriWorkspaceService.allWorkspaces[i];
|
||||||
|
if (ws.output === root.screenName)
|
||||||
|
displayWorkspaces.push(ws.idx + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
return displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplayActiveWorkspace() {
|
||||||
|
if (!NiriWorkspaceService.niriAvailable || NiriWorkspaceService.allWorkspaces.length === 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!root.screenName)
|
||||||
|
return NiriWorkspaceService.getCurrentWorkspaceNumber();
|
||||||
|
|
||||||
|
for (var i = 0; i < NiriWorkspaceService.allWorkspaces.length; i++) {
|
||||||
|
var ws = NiriWorkspaceService.allWorkspaces[i];
|
||||||
|
if (ws.output === root.screenName && ws.is_active)
|
||||||
|
return ws.idx + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
width: Math.max(120, workspaceRow.implicitWidth + Theme.spacingL * 2)
|
width: Math.max(120, workspaceRow.implicitWidth + Theme.spacingL * 2)
|
||||||
height: 30
|
height: 30
|
||||||
@@ -14,67 +49,29 @@ Rectangle {
|
|||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||||
visible: NiriWorkspaceService.niriAvailable
|
visible: NiriWorkspaceService.niriAvailable
|
||||||
|
|
||||||
property int currentWorkspace: getDisplayActiveWorkspace()
|
|
||||||
property var workspaceList: getDisplayWorkspaces()
|
|
||||||
|
|
||||||
function getDisplayWorkspaces() {
|
|
||||||
if (!NiriWorkspaceService.niriAvailable || NiriWorkspaceService.allWorkspaces.length === 0) {
|
|
||||||
return [1, 2]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.screenName) {
|
|
||||||
return NiriWorkspaceService.getCurrentOutputWorkspaceNumbers()
|
|
||||||
}
|
|
||||||
|
|
||||||
var displayWorkspaces = []
|
|
||||||
for (var i = 0; i < NiriWorkspaceService.allWorkspaces.length; i++) {
|
|
||||||
var ws = NiriWorkspaceService.allWorkspaces[i]
|
|
||||||
if (ws.output === root.screenName) {
|
|
||||||
displayWorkspaces.push(ws.idx + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2]
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDisplayActiveWorkspace() {
|
|
||||||
if (!NiriWorkspaceService.niriAvailable || NiriWorkspaceService.allWorkspaces.length === 0) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.screenName) {
|
|
||||||
return NiriWorkspaceService.getCurrentWorkspaceNumber()
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < NiriWorkspaceService.allWorkspaces.length; i++) {
|
|
||||||
var ws = NiriWorkspaceService.allWorkspaces[i]
|
|
||||||
if (ws.output === root.screenName && ws.is_active) {
|
|
||||||
return ws.idx + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NiriWorkspaceService
|
|
||||||
function onAllWorkspacesChanged() {
|
function onAllWorkspacesChanged() {
|
||||||
root.workspaceList = root.getDisplayWorkspaces()
|
root.workspaceList = root.getDisplayWorkspaces();
|
||||||
root.currentWorkspace = root.getDisplayActiveWorkspace()
|
root.currentWorkspace = root.getDisplayActiveWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFocusedWorkspaceIndexChanged() {
|
function onFocusedWorkspaceIndexChanged() {
|
||||||
root.currentWorkspace = root.getDisplayActiveWorkspace()
|
root.currentWorkspace = root.getDisplayActiveWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onNiriAvailableChanged() {
|
function onNiriAvailableChanged() {
|
||||||
if (NiriWorkspaceService.niriAvailable) {
|
if (NiriWorkspaceService.niriAvailable) {
|
||||||
root.workspaceList = root.getDisplayWorkspaces()
|
root.workspaceList = root.getDisplayWorkspaces();
|
||||||
root.currentWorkspace = root.getDisplayActiveWorkspace()
|
root.currentWorkspace = root.getDisplayActiveWorkspace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target: NiriWorkspaceService
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: workspaceRow
|
id: workspaceRow
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
@@ -89,15 +86,25 @@ Rectangle {
|
|||||||
width: isActive ? Theme.spacingXL + Theme.spacingM : Theme.spacingL + Theme.spacingXS
|
width: isActive ? Theme.spacingXL + Theme.spacingM : Theme.spacingL + Theme.spacingXS
|
||||||
height: Theme.spacingM
|
height: Theme.spacingM
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: isActive ? Theme.primary :
|
color: isActive ? Theme.primary : isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
||||||
isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5) :
|
|
||||||
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", sequentialNumber.toString()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -105,19 +112,13 @@ Rectangle {
|
|||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.emphasizedEasing
|
easing.type: Theme.emphasizedEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", sequentialNumber.toString()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -14,6 +14,16 @@ PanelWindow {
|
|||||||
property string wifiPasswordInput: ""
|
property string wifiPasswordInput: ""
|
||||||
|
|
||||||
visible: wifiPasswordDialogVisible
|
visible: wifiPasswordDialogVisible
|
||||||
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||||
|
color: "transparent"
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible)
|
||||||
|
passwordInput.forceActiveFocus();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
@@ -21,37 +31,27 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.layer: WlrLayershell.Overlay
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
passwordInput.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(0, 0, 0, 0.5)
|
color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
opacity: wifiPasswordDialogVisible ? 1.0 : 0.0
|
opacity: wifiPasswordDialogVisible ? 1 : 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
wifiPasswordDialogVisible = false;
|
||||||
|
wifiPasswordInput = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.mediumDuration
|
duration: Theme.mediumDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
wifiPasswordDialogVisible = false
|
|
||||||
wifiPasswordInput = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -62,23 +62,8 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadiusLarge
|
radius: Theme.cornerRadiusLarge
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
opacity: wifiPasswordDialogVisible ? 1 : 0
|
||||||
opacity: wifiPasswordDialogVisible ? 1.0 : 0.0
|
scale: wifiPasswordDialogVisible ? 1 : 0.9
|
||||||
scale: wifiPasswordDialogVisible ? 1.0 : 0.9
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -107,6 +92,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -125,15 +111,18 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: closeDialogArea
|
id: closeDialogArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
wifiPasswordDialogVisible = false
|
wifiPasswordDialogVisible = false;
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Password input
|
// Password input
|
||||||
@@ -147,6 +136,7 @@ PanelWindow {
|
|||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: passwordInput
|
id: passwordInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -155,6 +145,17 @@ PanelWindow {
|
|||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
cursorVisible: activeFocus
|
cursorVisible: activeFocus
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
onTextChanged: {
|
||||||
|
wifiPasswordInput = text;
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (wifiPasswordDialogVisible)
|
||||||
|
forceActiveFocus();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -165,28 +166,16 @@ PanelWindow {
|
|||||||
visible: parent.text.length === 0
|
visible: parent.text.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
wifiPasswordInput = text
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (wifiPasswordDialogVisible) {
|
|
||||||
forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.IBeamCursor
|
cursorShape: Qt.IBeamCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
passwordInput.forceActiveFocus()
|
passwordInput.forceActiveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show password checkbox
|
// Show password checkbox
|
||||||
@@ -195,6 +184,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: showPasswordCheckbox
|
id: showPasswordCheckbox
|
||||||
|
|
||||||
property bool checked: false
|
property bool checked: false
|
||||||
|
|
||||||
width: 20
|
width: 20
|
||||||
@@ -218,9 +208,10 @@ PanelWindow {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
showPasswordCheckbox.checked = !showPasswordCheckbox.checked
|
showPasswordCheckbox.checked = !showPasswordCheckbox.checked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -229,6 +220,7 @@ PanelWindow {
|
|||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
@@ -251,6 +243,7 @@ PanelWindow {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: cancelText
|
id: cancelText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Cancel"
|
text: "Cancel"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -260,14 +253,16 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: cancelArea
|
id: cancelArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
wifiPasswordDialogVisible = false
|
wifiPasswordDialogVisible = false;
|
||||||
wifiPasswordInput = ""
|
wifiPasswordInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -276,10 +271,11 @@ PanelWindow {
|
|||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
enabled: wifiPasswordInput.length > 0
|
enabled: wifiPasswordInput.length > 0
|
||||||
opacity: enabled ? 1.0 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: connectText
|
id: connectText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "Connect"
|
text: "Connect"
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
@@ -289,12 +285,13 @@ PanelWindow {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: connectArea
|
id: connectArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: {
|
onClicked: {
|
||||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput)
|
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,10 +300,33 @@ PanelWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
10
shell.qml
10
shell.qml
@@ -12,36 +12,46 @@ ShellRoot {
|
|||||||
// Multi-monitor support using Variants
|
// Multi-monitor support using Variants
|
||||||
Variants {
|
Variants {
|
||||||
model: Quickshell.screens
|
model: Quickshell.screens
|
||||||
|
|
||||||
delegate: TopBar {
|
delegate: TopBar {
|
||||||
modelData: item
|
modelData: item
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global popup windows
|
// Global popup windows
|
||||||
CenterCommandCenter {
|
CenterCommandCenter {
|
||||||
id: centerCommandCenter
|
id: centerCommandCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenuPopup {
|
TrayMenuPopup {
|
||||||
id: trayMenuPopup
|
id: trayMenuPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter {
|
NotificationCenter {
|
||||||
id: notificationCenter
|
id: notificationCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlCenterPopup {
|
ControlCenterPopup {
|
||||||
id: controlCenterPopup
|
id: controlCenterPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
WifiPasswordDialog {
|
WifiPasswordDialog {
|
||||||
id: wifiPasswordDialog
|
id: wifiPasswordDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
id: globalInputDialog
|
id: globalInputDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
BatteryControlPopup {
|
BatteryControlPopup {
|
||||||
id: batteryControlPopup
|
id: batteryControlPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerMenuPopup {
|
PowerMenuPopup {
|
||||||
id: powerMenuPopup
|
id: powerMenuPopup
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerConfirmDialog {
|
PowerConfirmDialog {
|
||||||
id: powerConfirmDialog
|
id: powerConfirmDialog
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user