1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-25 14:02:53 -05:00

Compare commits

..

1 Commits

Author SHA1 Message Date
bbedward
8e103d7ba7 Surface color overhaul 2025-09-25 11:35:43 -04:00
195 changed files with 6182 additions and 16432 deletions

View File

@@ -17,17 +17,6 @@ jobs:
with: with:
fetch-depth: 0 # Fetch full history for changelog generation fetch-depth: 0 # Fetch full history for changelog generation
# Create VERSION file
- name: Create VERSION file
run: |
echo "${{ github.ref_name }}" > VERSION
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add VERSION
git commit -m "Add VERSION file for ${{ github.ref_name }}"
git tag -f ${{ github.ref_name }}
git push origin ${{ github.ref_name }} --force
# Generate changelog # Generate changelog
- name: Generate Changelog - name: Generate Changelog
id: changelog id: changelog

167
CLAUDE.md
View File

@@ -63,9 +63,6 @@ quickshell -p shell.qml
# Or use the shorthand # Or use the shorthand
qs -p . qs -p .
# Run with verbose output for debugging
qs -v -p shell.qml
# Code formatting and linting # Code formatting and linting
qmlfmt -t 4 -i 4 -b 250 -w /path/to/file.qml # Format a QML file (requires qmlfmt, do not use qmlformat) qmlfmt -t 4 -i 4 -b 250 -w /path/to/file.qml # Format a QML file (requires qmlfmt, do not use qmlformat)
qmllint **/*.qml # Lint all QML files for syntax errors qmllint **/*.qml # Lint all QML files for syntax errors
@@ -92,7 +89,6 @@ shell.qml # Main entry point (minimal orchestration)
│ ├── DisplayService.qml │ ├── DisplayService.qml
│ ├── NotificationService.qml │ ├── NotificationService.qml
│ ├── WeatherService.qml │ ├── WeatherService.qml
│ ├── PluginService.qml
│ └── [14 more services] │ └── [14 more services]
├── Modules/ # UI components (93 files) ├── Modules/ # UI components (93 files)
│ ├── TopBar/ # Panel components (13 files) │ ├── TopBar/ # Panel components (13 files)
@@ -108,21 +104,15 @@ shell.qml # Main entry point (minimal orchestration)
│ ├── SettingsModal.qml │ ├── SettingsModal.qml
│ ├── ClipboardHistoryModal.qml │ ├── ClipboardHistoryModal.qml
│ ├── ProcessListModal.qml │ ├── ProcessListModal.qml
│ ├── PluginSettingsModal.qml
│ └── [7 more modals] │ └── [7 more modals]
── Widgets/ # Reusable UI controls (19 files) ── Widgets/ # Reusable UI controls (19 files)
├── DankIcon.qml ├── DankIcon.qml
├── DankSlider.qml ├── DankSlider.qml
├── DankToggle.qml ├── DankToggle.qml
├── DankTabBar.qml ├── DankTabBar.qml
├── DankGridView.qml ├── DankGridView.qml
├── DankListView.qml ├── DankListView.qml
└── [13 more widgets] └── [13 more widgets]
└── plugins/ # External plugins directory ($CONFIGPATH/DankMaterialShell/plugins/)
└── PluginName/ # Example Plugin structure
├── plugin.json # Plugin manifest
├── PluginNameWidget.qml # Widget component
└── PluginNameSettings.qml # Settings UI
``` ```
### Component Organization ### Component Organization
@@ -173,12 +163,6 @@ shell.qml # Main entry point (minimal orchestration)
- **DankLocationSearch**: Location picker with search - **DankLocationSearch**: Location picker with search
- **SystemLogo**: Animated system branding component - **SystemLogo**: Animated system branding component
7. **Plugins/** - External plugin system (`$CONFIGPATH/DankMaterialShell/plugins/`)
- **PluginService**: Discovers, loads, and manages plugin lifecycle
- **Dynamic Loading**: Plugins loaded at runtime from external directory
- **DankBar Integration**: Plugin widgets rendered alongside built-in widgets
- **Settings System**: Per-plugin settings with persistence
### Key Architectural Patterns ### Key Architectural Patterns
1. **Singleton Services Pattern**: 1. **Singleton Services Pattern**:
@@ -424,10 +408,10 @@ When modifying the shell:
Singleton { Singleton {
id: root id: root
property bool featureAvailable: false property bool featureAvailable: false
property type currentValue: defaultValue property type currentValue: defaultValue
function performAction(param) { function performAction(param) {
// Implementation // Implementation
} }
@@ -438,7 +422,7 @@ When modifying the shell:
```qml ```qml
// In module files // In module files
property alias serviceValue: NewService.currentValue property alias serviceValue: NewService.currentValue
SomeControl { SomeControl {
visible: NewService.featureAvailable visible: NewService.featureAvailable
enabled: NewService.featureAvailable enabled: NewService.featureAvailable
@@ -446,134 +430,6 @@ When modifying the shell:
} }
``` ```
### Creating Plugins
Plugins are external, dynamically-loaded components that extend DankBar functionality. Plugins are stored in `~/.config/DankMaterialShell/plugins/` and have their settings isolated from core DMS settings.
1. **Create plugin directory**:
```bash
mkdir -p ~/.config/DankMaterialShell/plugins/YourPlugin
```
2. **Create manifest** (`plugin.json`):
```json
{
"id": "yourPlugin",
"name": "Your Plugin",
"description": "Widget description",
"version": "1.0.0",
"author": "Your Name",
"icon": "extension",
"component": "./YourWidget.qml",
"settings": "./YourSettings.qml",
"permissions": ["settings_read", "settings_write"]
}
```
3. **Create widget component** (`YourWidget.qml`):
```qml
import QtQuick
import qs.Services
Rectangle {
id: root
property bool compactMode: false
property string section: "center"
property real widgetHeight: 30
property var pluginService: null
width: content.implicitWidth + 16
height: widgetHeight
radius: 8
color: "#20FFFFFF"
Component.onCompleted: {
if (pluginService) {
var data = pluginService.loadPluginData("yourPlugin", "key", defaultValue)
}
}
}
```
4. **Create settings component** (`YourSettings.qml`):
```qml
import QtQuick
import QtQuick.Controls
FocusScope {
id: root
property var pluginService: null
implicitHeight: settingsColumn.implicitHeight
height: implicitHeight
Column {
id: settingsColumn
anchors.fill: parent
anchors.margins: 16
spacing: 12
Text {
text: "Your Plugin Settings"
font.pixelSize: 18
font.weight: Font.Bold
}
// Your settings UI here
}
function saveSettings(key, value) {
if (pluginService) {
pluginService.savePluginData("yourPlugin", key, value)
}
}
function loadSettings(key, defaultValue) {
if (pluginService) {
return pluginService.loadPluginData("yourPlugin", key, defaultValue)
}
return defaultValue
}
}
```
5. **Enable plugin**:
- Open Settings → Plugins
- Click "Scan for Plugins"
- Toggle plugin to enable
- Add plugin ID to DankBar widget list
**Plugin Directory Structure:**
```
~/.config/DankMaterialShell/
├── settings.json # Core DMS settings + plugin settings
│ └── pluginSettings: {
│ └── yourPlugin: {
│ ├── enabled: true,
│ └── customData: {...}
│ }
│ }
└── plugins/ # Plugin files directory
└── YourPlugin/ # Plugin directory (matches manifest ID)
├── plugin.json # Plugin manifest
├── YourWidget.qml # Widget component
└── YourSettings.qml # Settings UI (optional)
```
**Key Plugin APIs:**
- `pluginService.loadPluginData(pluginId, key, default)` - Load persistent data
- `pluginService.savePluginData(pluginId, key, value)` - Save persistent data
- `PluginService.enablePlugin(pluginId)` - Load plugin
- `PluginService.disablePlugin(pluginId)` - Unload plugin
**Important Notes:**
- Plugin settings are automatically injected by the PluginService via `item.pluginService = PluginService`
- Settings are stored in the main settings.json but namespaced under `pluginSettings.{pluginId}`
- Plugin directories must match the plugin ID in the manifest
- Use the injected `pluginService` property in both widget and settings components
### Debugging Common Issues ### Debugging Common Issues
1. **Import errors**: Check import paths 1. **Import errors**: Check import paths
@@ -598,7 +454,6 @@ Plugins are external, dynamically-loaded components that extend DankBar function
- **Function Discovery**: Use grep/search tools to find existing utility functions before implementing new ones - **Function Discovery**: Use grep/search tools to find existing utility functions before implementing new ones
- **Modern QML Patterns**: Leverage new widgets like DankTextField, DankDropdown, CachingImage - **Modern QML Patterns**: Leverage new widgets like DankTextField, DankDropdown, CachingImage
- **Structured Organization**: Follow the established Services/Modules/Widgets/Modals separation - **Structured Organization**: Follow the established Services/Modules/Widgets/Modals separation
- **Plugin System**: For user extensions, create plugins instead of modifying core modules - see docs/PLUGINS.md
### Common Widget Patterns ### Common Widget Patterns

View File

@@ -1,6 +1,6 @@
# Contributing # Contributing
Contributions are welcome and encouraged. Contributions are welcome and encourages.
## Formatting ## Formatting
@@ -27,4 +27,4 @@ Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you m
## Pull request ## Pull request
Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting. Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting.

View File

@@ -1,53 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Singleton {
id: root
readonly property var facts: [
"A photon takes 100,000 to 200,000 years bouncing through the Sun's dense core, then races to Earth in just 8 minutes 20 seconds.",
"A teaspoon of neutron star matter would weigh a billion metric tons here on Earth.",
"Right now, 100 trillion solar neutrinos are passing through your body every second.",
"The Sun converts 4 million metric tons of matter into pure energy every second—enough to power Earth for 500,000 years.",
"The universe still glows with leftover heat from the Big Bang—just 2.7 degrees above absolute zero.",
"There's a nebula out there that's actually colder than empty space itself.",
"We've detected black holes crashing together by measuring spacetime stretch by less than 1/10,000th the width of a proton.",
"Fast radio bursts can release more energy in 5 milliseconds than our Sun produces in 3 days.",
"Our galaxy might be crawling with billions of rogue planets drifting alone in the dark.",
"Distant galaxies can move away from us faster than light because space itself is stretching.",
"The edge of what we can see is 46.5 billion light-years away, even though the universe is only 13.8 billion years old.",
"The universe is mostly invisible: 5% regular matter, 27% dark matter, 68% dark energy.",
"A day on Venus lasts longer than its entire year around the Sun.",
"On Mercury, the time between sunrises is 176 Earth days long.",
"In about 4.5 billion years, our galaxy will smash into Andromeda.",
"Most of the gold in your jewelry was forged when neutron stars collided somewhere in space.",
"PSR J1748-2446ad, the fastest spinning star, rotates 716 times per second—its equator moves at 24% the speed of light.",
"Cosmic rays create particles that shouldn't make it to Earth's surface, but time dilation lets them sneak through.",
"Jupiter's magnetic field is so huge that if we could see it, it would look bigger than the Moon in our sky.",
"Interstellar space is so empty it's like a cube 32 kilometers wide containing just a single grain of sand.",
"Voyager 1 is 24 billion kilometers away but won't leave the Sun's gravitational influence for another 30,000 years.",
"Counting to a billion at one number per second would take over 31 years.",
"Space is so vast, even speeding at light-speed, you'd never return past the cosmic horizon.",
"Astronauts on the ISS age about 0.01 seconds less each year than people on Earth.",
"Sagittarius B2, a dust cloud near our galaxy's center, contains ethyl formate—the compound that gives raspberries their flavor and rum its smell.",
"Beyond 16 billion light-years, the cosmic event horizon marks where space expands too fast for light to ever reach us again.",
"Even at light-speed, you'd never catch up to most galaxies—space expands faster.",
"Only around 5% of galaxies are ever reachable—even at light-speed.",
"If the Sun vanished, we'd still orbit it for 8 minutes before drifting away.",
"If a planet 65 million light-years away looked at Earth now, it'd see dinosaurs.",
"Our oldest radio signals will reach the Milky Way's center in 26,000 years.",
"Every atom in your body heavier than hydrogen was forged in the nuclear furnace of a dying star.",
"The Moon moves 3.8 centimeters farther from Earth every year.",
"The universe creates 275 million new stars every single day.",
"Jupiter's Great Red Spot is a storm twice the size of Earth that has been raging for at least 350 years.",
"If you watched someone fall into a black hole, they'd appear frozen at the event horizon forever—time effectively stops from your perspective.",
"The Boötes Supervoid is a cosmic desert 1.8 billion light-years across with 60% fewer galaxies than it should have."
]
function getRandomFact() {
return facts[Math.floor(Math.random() * facts.length)]
}
}

View File

@@ -12,19 +12,12 @@ Singleton {
id: root id: root
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
property bool isLightMode: false property bool isLightMode: false
property string wallpaperPath: "" property string wallpaperPath: ""
property string wallpaperLastPath: "" property string wallpaperLastPath: ""
property string profileLastPath: "" property string profileLastPath: ""
property bool perMonitorWallpaper: false property bool perMonitorWallpaper: false
property var monitorWallpapers: ({}) property var monitorWallpapers: ({})
property bool perModeWallpaper: false
property string wallpaperPathLight: ""
property string wallpaperPathDark: ""
property var monitorWallpapersLight: ({})
property var monitorWallpapersDark: ({})
property bool doNotDisturb: false property bool doNotDisturb: false
property bool nightModeEnabled: false property bool nightModeEnabled: false
property int nightModeTemperature: 4500 property int nightModeTemperature: 4500
@@ -73,17 +66,11 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
if (!isGreeterMode) { loadSettings()
loadSettings()
}
} }
function loadSettings() { function loadSettings() {
if (isGreeterMode) { parseSettings(settingsFile.text())
parseSettings(greeterSessionFile.text())
} else {
parseSettings(settingsFile.text())
}
} }
function parseSettings(content) { function parseSettings(content) {
@@ -96,11 +83,6 @@ Singleton {
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : "" profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : ""
perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false
monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {} monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {}
perModeWallpaper = settings.perModeWallpaper !== undefined ? settings.perModeWallpaper : false
wallpaperPathLight = settings.wallpaperPathLight !== undefined ? settings.wallpaperPathLight : ""
wallpaperPathDark = settings.wallpaperPathDark !== undefined ? settings.wallpaperPathDark : ""
monitorWallpapersLight = settings.monitorWallpapersLight !== undefined ? settings.monitorWallpapersLight : {}
monitorWallpapersDark = settings.monitorWallpapersDark !== undefined ? settings.monitorWallpapersDark : {}
doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500 nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
@@ -150,11 +132,10 @@ Singleton {
batterySuspendTimeout = settings.batterySuspendTimeout !== undefined ? settings.batterySuspendTimeout : 0 batterySuspendTimeout = settings.batterySuspendTimeout !== undefined ? settings.batterySuspendTimeout : 0
batteryHibernateTimeout = settings.batteryHibernateTimeout !== undefined ? settings.batteryHibernateTimeout : 0 batteryHibernateTimeout = settings.batteryHibernateTimeout !== undefined ? settings.batteryHibernateTimeout : 0
lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false
if (!isGreeterMode) { // Generate system themes but don't override user's theme choice
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
}
} }
} }
} catch (e) { } catch (e) {
@@ -163,7 +144,6 @@ Singleton {
} }
function saveSettings() { function saveSettings() {
if (isGreeterMode) return
settingsFile.setText(JSON.stringify({ settingsFile.setText(JSON.stringify({
"isLightMode": isLightMode, "isLightMode": isLightMode,
"wallpaperPath": wallpaperPath, "wallpaperPath": wallpaperPath,
@@ -171,11 +151,6 @@ Singleton {
"profileLastPath": profileLastPath, "profileLastPath": profileLastPath,
"perMonitorWallpaper": perMonitorWallpaper, "perMonitorWallpaper": perMonitorWallpaper,
"monitorWallpapers": monitorWallpapers, "monitorWallpapers": monitorWallpapers,
"perModeWallpaper": perModeWallpaper,
"wallpaperPathLight": wallpaperPathLight,
"wallpaperPathDark": wallpaperPathDark,
"monitorWallpapersLight": monitorWallpapersLight,
"monitorWallpapersDark": monitorWallpapersDark,
"doNotDisturb": doNotDisturb, "doNotDisturb": doNotDisturb,
"nightModeEnabled": nightModeEnabled, "nightModeEnabled": nightModeEnabled,
"nightModeTemperature": nightModeTemperature, "nightModeTemperature": nightModeTemperature,
@@ -216,21 +191,9 @@ Singleton {
function setLightMode(lightMode) { function setLightMode(lightMode) {
isLightMode = lightMode isLightMode = lightMode
syncWallpaperForCurrentMode()
saveSettings() saveSettings()
} }
function syncWallpaperForCurrentMode() {
if (!perModeWallpaper) return
if (perMonitorWallpaper) {
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
return
}
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
}
function setDoNotDisturb(enabled) { function setDoNotDisturb(enabled) {
doNotDisturb = enabled doNotDisturb = enabled
saveSettings() saveSettings()
@@ -301,32 +264,24 @@ Singleton {
function setWallpaper(imagePath) { function setWallpaper(imagePath) {
wallpaperPath = imagePath wallpaperPath = imagePath
if (perModeWallpaper) {
if (isLightMode) {
wallpaperPathLight = imagePath
} else {
wallpaperPathDark = imagePath
}
}
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
if (Theme.currentTheme === Theme.dynamic) {
Theme.extractColors()
}
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
} }
function setWallpaperColor(color) { function setWallpaperColor(color) {
wallpaperPath = color wallpaperPath = color
if (perModeWallpaper) {
if (isLightMode) {
wallpaperPathLight = color
} else {
wallpaperPathDark = color
}
}
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
if (Theme.currentTheme === Theme.dynamic) {
Theme.extractColors()
}
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
} }
@@ -471,52 +426,13 @@ Singleton {
function setPerMonitorWallpaper(enabled) { function setPerMonitorWallpaper(enabled) {
perMonitorWallpaper = enabled perMonitorWallpaper = enabled
if (enabled && perModeWallpaper) {
syncWallpaperForCurrentMode()
}
saveSettings() saveSettings()
// Refresh dynamic theming when per-monitor mode changes
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
Theme.generateSystemThemesFromCurrentTheme() if (Theme.currentTheme === Theme.dynamic) {
} Theme.extractColors()
}
function setPerModeWallpaper(enabled) {
if (enabled && wallpaperCyclingEnabled) {
setWallpaperCyclingEnabled(false)
}
if (enabled && perMonitorWallpaper) {
var monitorCyclingAny = false
for (var key in monitorCyclingSettings) {
if (monitorCyclingSettings[key].enabled) {
monitorCyclingAny = true
break
}
} }
if (monitorCyclingAny) {
var newSettings = Object.assign({}, monitorCyclingSettings)
for (var screenName in newSettings) {
newSettings[screenName].enabled = false
}
monitorCyclingSettings = newSettings
}
}
perModeWallpaper = enabled
if (enabled) {
if (perMonitorWallpaper) {
monitorWallpapersLight = Object.assign({}, monitorWallpapers)
monitorWallpapersDark = Object.assign({}, monitorWallpapers)
} else {
wallpaperPathLight = wallpaperPath
wallpaperPathDark = wallpaperPath
}
} else {
syncWallpaperForCurrentMode()
}
saveSettings()
if (typeof Theme !== "undefined") {
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
} }
@@ -529,32 +445,15 @@ Singleton {
delete newMonitorWallpapers[screenName] delete newMonitorWallpapers[screenName]
} }
monitorWallpapers = newMonitorWallpapers monitorWallpapers = newMonitorWallpapers
if (perModeWallpaper) {
if (isLightMode) {
var newLight = Object.assign({}, monitorWallpapersLight)
if (path && path !== "") {
newLight[screenName] = path
} else {
delete newLight[screenName]
}
monitorWallpapersLight = newLight
} else {
var newDark = Object.assign({}, monitorWallpapersDark)
if (path && path !== "") {
newDark[screenName] = path
} else {
delete newDark[screenName]
}
monitorWallpapersDark = newDark
}
}
saveSettings() saveSettings()
// Trigger dynamic theming if this is the first monitor and dynamic theming is enabled
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") { if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
var screens = Quickshell.screens var screens = Quickshell.screens
if (screens.length > 0 && screenName === screens[0].name) { if (screens.length > 0 && screenName === screens[0].name) {
if (Theme.currentTheme === Theme.dynamic) {
Theme.extractColors()
}
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
} }
@@ -630,43 +529,22 @@ Singleton {
FileView { FileView {
id: settingsFile id: settingsFile
path: isGreeterMode ? "" : StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json" path: StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json"
blockLoading: isGreeterMode blockLoading: true
blockWrites: true blockWrites: true
watchChanges: !isGreeterMode watchChanges: true
onLoaded: { onLoaded: {
if (!isGreeterMode) { parseSettings(settingsFile.text())
parseSettings(settingsFile.text()) hasTriedDefaultSession = false
hasTriedDefaultSession = false
}
} }
onLoadFailed: error => { onLoadFailed: error => {
if (!isGreeterMode && !hasTriedDefaultSettings) { if (!hasTriedDefaultSession) {
hasTriedDefaultSettings = true hasTriedDefaultSession = true
defaultSessionCheckProcess.running = true defaultSessionCheckProcess.running = true
} }
} }
} }
FileView {
id: greeterSessionFile
path: {
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
return greetCfgDir + "/session.json"
}
preload: isGreeterMode
blockLoading: false
blockWrites: true
watchChanges: false
printErrors: true
onLoaded: {
if (isGreeterMode) {
parseSettings(greeterSessionFile.text())
}
}
}
Process { Process {
id: defaultSessionCheckProcess id: defaultSessionCheckProcess

View File

@@ -12,30 +12,13 @@ import qs.Services
Singleton { Singleton {
id: root id: root
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
enum Position {
Top,
Bottom,
Left,
Right
}
enum AnimationSpeed {
None,
Shortest,
Short,
Medium,
Long
}
// Theme settings // Theme settings
property string currentThemeName: "blue" property string currentThemeName: "blue"
property string customThemeFile: "" property string customThemeFile: ""
property string matugenScheme: "scheme-tonal-spot" property string matugenScheme: "scheme-tonal-spot"
property real dankBarTransparency: 1.0 property real topBarTransparency: 0.75
property real dankBarWidgetTransparency: 1.0 property real topBarWidgetTransparency: 0.85
property real popupTransparency: 1.0 property real popupTransparency: 0.92
property real dockTransparency: 1 property real dockTransparency: 1
property bool use24HourClock: true property bool use24HourClock: true
property bool useFahrenheit: false property bool useFahrenheit: false
@@ -87,13 +70,12 @@ Singleton {
property string clockDateFormat: "" property string clockDateFormat: ""
property string lockDateFormat: "" property string lockDateFormat: ""
property int mediaSize: 1 property int mediaSize: 1
property var dankBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"] property var topBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"]
property var dankBarCenterWidgets: ["music", "clock", "weather"] property var topBarCenterWidgets: ["music", "clock", "weather"]
property var dankBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"] property var topBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
property var dankBarWidgetOrder: [] property alias topBarLeftWidgetsModel: leftWidgetsModel
property alias dankBarLeftWidgetsModel: leftWidgetsModel property alias topBarCenterWidgetsModel: centerWidgetsModel
property alias dankBarCenterWidgetsModel: centerWidgetsModel property alias topBarRightWidgetsModel: rightWidgetsModel
property alias dankBarRightWidgetsModel: rightWidgetsModel
property string appLauncherViewMode: "list" property string appLauncherViewMode: "list"
property string spotlightModalViewMode: "list" property string spotlightModalViewMode: "list"
property string networkPreference: "auto" property string networkPreference: "auto"
@@ -135,48 +117,37 @@ Singleton {
property bool showDock: false property bool showDock: false
property bool dockAutoHide: false property bool dockAutoHide: false
property bool dockGroupByApp: false property bool dockGroupByApp: false
property bool dockOpenOnOverview: false
property int dockPosition: SettingsData.Position.Bottom
property real dockSpacing: 4
property real dockBottomGap: 0
property real cornerRadius: 12 property real cornerRadius: 12
property bool notificationOverlayEnabled: false property bool notificationOverlayEnabled: false
property bool dankBarAutoHide: false property bool topBarAutoHide: false
property bool dankBarOpenOnOverview: false property bool topBarOpenOnOverview: false
property bool dankBarVisible: true property bool topBarVisible: true
property real dankBarSpacing: 4 property real topBarSpacing: 4
property real dankBarBottomGap: 0 property real topBarBottomGap: 0
property real dankBarInnerPadding: 4 property real topBarInnerPadding: 8
property bool dankBarSquareCorners: false property bool topBarSquareCorners: false
property bool dankBarNoBackground: false property bool topBarNoBackground: false
property bool dankBarGothCornersEnabled: false property bool topBarGothCornersEnabled: false
property int dankBarPosition: SettingsData.Position.Top
property bool dankBarIsVertical: dankBarPosition === SettingsData.Position.Left || dankBarPosition === SettingsData.Position.Right
property bool lockScreenShowPowerActions: true property bool lockScreenShowPowerActions: true
property bool hideBrightnessSlider: false property bool hideBrightnessSlider: false
property string widgetBackgroundColor: "sch" property string widgetBackgroundColor: "sch"
property string surfaceBase: "s" property string surfaceBase: "sc"
property int notificationTimeoutLow: 5000 property int notificationTimeoutLow: 5000
property int notificationTimeoutNormal: 5000 property int notificationTimeoutNormal: 5000
property int notificationTimeoutCritical: 0 property int notificationTimeoutCritical: 0
property int notificationPopupPosition: SettingsData.Position.Top
property var screenPreferences: ({}) property var screenPreferences: ({})
property int animationSpeed: SettingsData.AnimationSpeed.Short
readonly property string defaultFontFamily: "Inter Variable" readonly property string defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code" readonly property string defaultMonoFontFamily: "Fira Code"
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation) readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation) readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation)
readonly property string _configDir: Paths.strip(_configUrl) readonly property string _configDir: Paths.strip(_configUrl)
signal forceDankBarLayoutRefresh signal forceTopBarLayoutRefresh
signal forceDockLayoutRefresh
signal widgetDataChanged signal widgetDataChanged
signal workspaceIconsUpdated signal workspaceIconsUpdated
property bool _loading: false property bool _loading: false
property var pluginSettings: ({})
function getEffectiveTimeFormat() { function getEffectiveTimeFormat() {
if (use24HourClock) { if (use24HourClock) {
return Locale.ShortFormat return Locale.ShortFormat
@@ -208,9 +179,9 @@ Singleton {
centerWidgetsModel.append(dummyItem) centerWidgetsModel.append(dummyItem)
rightWidgetsModel.append(dummyItem) rightWidgetsModel.append(dummyItem)
updateListModel(leftWidgetsModel, dankBarLeftWidgets) updateListModel(leftWidgetsModel, topBarLeftWidgets)
updateListModel(centerWidgetsModel, dankBarCenterWidgets) updateListModel(centerWidgetsModel, topBarCenterWidgets)
updateListModel(rightWidgetsModel, dankBarRightWidgets) updateListModel(rightWidgetsModel, topBarRightWidgets)
} }
function loadSettings() { function loadSettings() {
@@ -238,9 +209,9 @@ Singleton {
} }
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "" customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot" matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
dankBarTransparency = settings.dankBarTransparency !== undefined ? (settings.dankBarTransparency > 1 ? settings.dankBarTransparency / 100 : settings.dankBarTransparency) : (settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 1.0) topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75
dankBarWidgetTransparency = settings.dankBarWidgetTransparency !== undefined ? (settings.dankBarWidgetTransparency > 1 ? settings.dankBarWidgetTransparency / 100 : settings.dankBarWidgetTransparency) : (settings.topBarWidgetTransparency !== undefined ? (settings.topBarWidgetTransparency > 1 ? settings.topBarWidgetTransparency / 100 : settings.topBarWidgetTransparency) : 1.0) topBarWidgetTransparency = settings.topBarWidgetTransparency !== undefined ? (settings.topBarWidgetTransparency > 1 ? settings.topBarWidgetTransparency / 100 : settings.topBarWidgetTransparency) : 0.85
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 1.0 popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 0.92
dockTransparency = settings.dockTransparency !== undefined ? (settings.dockTransparency > 1 ? settings.dockTransparency / 100 : settings.dockTransparency) : 1 dockTransparency = settings.dockTransparency !== undefined ? (settings.dockTransparency > 1 ? settings.dockTransparency / 100 : settings.dockTransparency) : 1
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
@@ -293,24 +264,23 @@ Singleton {
clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : "" clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : ""
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "" lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1) mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1)
if (settings.dankBarWidgetOrder || settings.topBarWidgetOrder) { if (settings.topBarWidgetOrder) {
var widgetOrder = settings.dankBarWidgetOrder || settings.topBarWidgetOrder topBarLeftWidgets = settings.topBarWidgetOrder.filter(w => {
dankBarLeftWidgets = widgetOrder.filter(w => {
return ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w) return ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w)
}) })
dankBarCenterWidgets = widgetOrder.filter(w => { topBarCenterWidgets = settings.topBarWidgetOrder.filter(w => {
return ["clock", "music", "weather"].includes(w) return ["clock", "music", "weather"].includes(w)
}) })
dankBarRightWidgets = widgetOrder.filter(w => { topBarRightWidgets = settings.topBarWidgetOrder.filter(w => {
return ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w) return ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w)
}) })
} else { } else {
var leftWidgets = settings.dankBarLeftWidgets !== undefined ? settings.dankBarLeftWidgets : (settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"]) var leftWidgets = settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"]
var centerWidgets = settings.dankBarCenterWidgets !== undefined ? settings.dankBarCenterWidgets : (settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"]) var centerWidgets = settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"]
var rightWidgets = settings.dankBarRightWidgets !== undefined ? settings.dankBarRightWidgets : (settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]) var rightWidgets = settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
dankBarLeftWidgets = leftWidgets topBarLeftWidgets = leftWidgets
dankBarCenterWidgets = centerWidgets topBarCenterWidgets = centerWidgets
dankBarRightWidgets = rightWidgets topBarRightWidgets = rightWidgets
updateListModel(leftWidgetsModel, leftWidgets) updateListModel(leftWidgetsModel, leftWidgets)
updateListModel(centerWidgetsModel, centerWidgets) updateListModel(centerWidgetsModel, centerWidgets)
updateListModel(rightWidgetsModel, rightWidgets) updateListModel(rightWidgetsModel, rightWidgets)
@@ -338,33 +308,25 @@ Singleton {
showDock = settings.showDock !== undefined ? settings.showDock : false showDock = settings.showDock !== undefined ? settings.showDock : false
dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false
dockGroupByApp = settings.dockGroupByApp !== undefined ? settings.dockGroupByApp : false dockGroupByApp = settings.dockGroupByApp !== undefined ? settings.dockGroupByApp : false
dockPosition = settings.dockPosition !== undefined ? settings.dockPosition : SettingsData.Position.Bottom
dockSpacing = settings.dockSpacing !== undefined ? settings.dockSpacing : 4
dockBottomGap = settings.dockBottomGap !== undefined ? settings.dockBottomGap : 0
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12 cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
dankBarAutoHide = settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : (settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false) topBarAutoHide = settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false
dankBarOpenOnOverview = settings.dankBarOpenOnOverview !== undefined ? settings.dankBarOpenOnOverview : (settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false) topBarOpenOnOverview = settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false
dankBarVisible = settings.dankBarVisible !== undefined ? settings.dankBarVisible : (settings.topBarVisible !== undefined ? settings.topBarVisible : true) topBarVisible = settings.topBarVisible !== undefined ? settings.topBarVisible : true
dockOpenOnOverview = settings.dockOpenOnOverview !== undefined ? settings.dockOpenOnOverview : false
notificationTimeoutLow = settings.notificationTimeoutLow !== undefined ? settings.notificationTimeoutLow : 5000 notificationTimeoutLow = settings.notificationTimeoutLow !== undefined ? settings.notificationTimeoutLow : 5000
notificationTimeoutNormal = settings.notificationTimeoutNormal !== undefined ? settings.notificationTimeoutNormal : 5000 notificationTimeoutNormal = settings.notificationTimeoutNormal !== undefined ? settings.notificationTimeoutNormal : 5000
notificationTimeoutCritical = settings.notificationTimeoutCritical !== undefined ? settings.notificationTimeoutCritical : 0 notificationTimeoutCritical = settings.notificationTimeoutCritical !== undefined ? settings.notificationTimeoutCritical : 0
notificationPopupPosition = settings.notificationPopupPosition !== undefined ? settings.notificationPopupPosition : SettingsData.Position.Top topBarSpacing = settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4
dankBarSpacing = settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : (settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4) topBarBottomGap = settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0
dankBarBottomGap = settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : (settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0) topBarInnerPadding = settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 8
dankBarInnerPadding = settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : (settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 4) topBarSquareCorners = settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false
dankBarSquareCorners = settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : (settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false) topBarNoBackground = settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : false
dankBarNoBackground = settings.dankBarNoBackground !== undefined ? settings.dankBarNoBackground : (settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : false) topBarGothCornersEnabled = settings.topBarGothCornersEnabled !== undefined ? settings.topBarGothCornersEnabled : false
dankBarGothCornersEnabled = settings.dankBarGothCornersEnabled !== undefined ? settings.dankBarGothCornersEnabled : (settings.topBarGothCornersEnabled !== undefined ? settings.topBarGothCornersEnabled : false)
dankBarPosition = settings.dankBarPosition !== undefined ? settings.dankBarPosition : (settings.dankBarAtBottom !== undefined ? (settings.dankBarAtBottom ? SettingsData.Position.Bottom : SettingsData.Position.Top) : (settings.topBarAtBottom !== undefined ? (settings.topBarAtBottom ? SettingsData.Position.Bottom : SettingsData.Position.Top) : SettingsData.Position.Top))
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true
hideBrightnessSlider = settings.hideBrightnessSlider !== undefined ? settings.hideBrightnessSlider : false hideBrightnessSlider = settings.hideBrightnessSlider !== undefined ? settings.hideBrightnessSlider : false
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch" widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch"
surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s" surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "sc"
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({}) screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
pluginSettings = settings.pluginSettings !== undefined ? settings.pluginSettings : ({})
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
applyStoredTheme() applyStoredTheme()
detectAvailableIconThemes() detectAvailableIconThemes()
detectQtTools() detectQtTools()
@@ -387,8 +349,8 @@ Singleton {
"currentThemeName": currentThemeName, "currentThemeName": currentThemeName,
"customThemeFile": customThemeFile, "customThemeFile": customThemeFile,
"matugenScheme": matugenScheme, "matugenScheme": matugenScheme,
"dankBarTransparency": dankBarTransparency, "topBarTransparency": topBarTransparency,
"dankBarWidgetTransparency": dankBarWidgetTransparency, "topBarWidgetTransparency": topBarWidgetTransparency,
"popupTransparency": popupTransparency, "popupTransparency": popupTransparency,
"dockTransparency": dockTransparency, "dockTransparency": dockTransparency,
"use24HourClock": use24HourClock, "use24HourClock": use24HourClock,
@@ -433,9 +395,9 @@ Singleton {
"clockDateFormat": clockDateFormat, "clockDateFormat": clockDateFormat,
"lockDateFormat": lockDateFormat, "lockDateFormat": lockDateFormat,
"mediaSize": mediaSize, "mediaSize": mediaSize,
"dankBarLeftWidgets": dankBarLeftWidgets, "topBarLeftWidgets": topBarLeftWidgets,
"dankBarCenterWidgets": dankBarCenterWidgets, "topBarCenterWidgets": topBarCenterWidgets,
"dankBarRightWidgets": dankBarRightWidgets, "topBarRightWidgets": topBarRightWidgets,
"appLauncherViewMode": appLauncherViewMode, "appLauncherViewMode": appLauncherViewMode,
"spotlightModalViewMode": spotlightModalViewMode, "spotlightModalViewMode": spotlightModalViewMode,
"networkPreference": networkPreference, "networkPreference": networkPreference,
@@ -459,22 +421,17 @@ Singleton {
"showDock": showDock, "showDock": showDock,
"dockAutoHide": dockAutoHide, "dockAutoHide": dockAutoHide,
"dockGroupByApp": dockGroupByApp, "dockGroupByApp": dockGroupByApp,
"dockOpenOnOverview": dockOpenOnOverview,
"dockPosition": dockPosition,
"dockSpacing": dockSpacing,
"dockBottomGap": dockBottomGap,
"cornerRadius": cornerRadius, "cornerRadius": cornerRadius,
"notificationOverlayEnabled": notificationOverlayEnabled, "notificationOverlayEnabled": notificationOverlayEnabled,
"dankBarAutoHide": dankBarAutoHide, "topBarAutoHide": topBarAutoHide,
"dankBarOpenOnOverview": dankBarOpenOnOverview, "topBarOpenOnOverview": topBarOpenOnOverview,
"dankBarVisible": dankBarVisible, "topBarVisible": topBarVisible,
"dankBarSpacing": dankBarSpacing, "topBarSpacing": topBarSpacing,
"dankBarBottomGap": dankBarBottomGap, "topBarBottomGap": topBarBottomGap,
"dankBarInnerPadding": dankBarInnerPadding, "topBarInnerPadding": topBarInnerPadding,
"dankBarSquareCorners": dankBarSquareCorners, "topBarSquareCorners": topBarSquareCorners,
"dankBarNoBackground": dankBarNoBackground, "topBarNoBackground": topBarNoBackground,
"dankBarGothCornersEnabled": dankBarGothCornersEnabled, "topBarGothCornersEnabled": topBarGothCornersEnabled,
"dankBarPosition": dankBarPosition,
"lockScreenShowPowerActions": lockScreenShowPowerActions, "lockScreenShowPowerActions": lockScreenShowPowerActions,
"hideBrightnessSlider": hideBrightnessSlider, "hideBrightnessSlider": hideBrightnessSlider,
"widgetBackgroundColor": widgetBackgroundColor, "widgetBackgroundColor": widgetBackgroundColor,
@@ -482,10 +439,7 @@ Singleton {
"notificationTimeoutLow": notificationTimeoutLow, "notificationTimeoutLow": notificationTimeoutLow,
"notificationTimeoutNormal": notificationTimeoutNormal, "notificationTimeoutNormal": notificationTimeoutNormal,
"notificationTimeoutCritical": notificationTimeoutCritical, "notificationTimeoutCritical": notificationTimeoutCritical,
"notificationPopupPosition": notificationPopupPosition, "screenPreferences": screenPreferences
"screenPreferences": screenPreferences,
"pluginSettings": pluginSettings,
"animationSpeed": animationSpeed
}, null, 2)) }, null, 2))
} }
@@ -601,11 +555,11 @@ Singleton {
function applyStoredTheme() { function applyStoredTheme() {
if (typeof Theme !== "undefined") if (typeof Theme !== "undefined")
Theme.switchTheme(currentThemeName, false, false) Theme.switchTheme(currentThemeName, false)
else else
Qt.callLater(() => { Qt.callLater(() => {
if (typeof Theme !== "undefined") if (typeof Theme !== "undefined")
Theme.switchTheme(currentThemeName, false, false) Theme.switchTheme(currentThemeName, false)
}) })
} }
@@ -628,17 +582,20 @@ Singleton {
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
if (Theme.currentTheme === Theme.dynamic) {
Theme.extractColors()
}
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
} }
function setDankBarTransparency(transparency) { function setTopBarTransparency(transparency) {
dankBarTransparency = transparency topBarTransparency = transparency
saveSettings() saveSettings()
} }
function setDankBarWidgetTransparency(transparency) { function setTopBarWidgetTransparency(transparency) {
dankBarWidgetTransparency = transparency topBarWidgetTransparency = transparency
saveSettings() saveSettings()
} }
@@ -773,25 +730,25 @@ Singleton {
saveSettings() saveSettings()
} }
function setDankBarWidgetOrder(order) { function setTopBarWidgetOrder(order) {
dankBarWidgetOrder = order topBarWidgetOrder = order
saveSettings() saveSettings()
} }
function setDankBarLeftWidgets(order) { function setTopBarLeftWidgets(order) {
dankBarLeftWidgets = order topBarLeftWidgets = order
updateListModel(leftWidgetsModel, order) updateListModel(leftWidgetsModel, order)
saveSettings() saveSettings()
} }
function setDankBarCenterWidgets(order) { function setTopBarCenterWidgets(order) {
dankBarCenterWidgets = order topBarCenterWidgets = order
updateListModel(centerWidgetsModel, order) updateListModel(centerWidgetsModel, order)
saveSettings() saveSettings()
} }
function setDankBarRightWidgets(order) { function setTopBarRightWidgets(order) {
dankBarRightWidgets = order topBarRightWidgets = order
updateListModel(rightWidgetsModel, order) updateListModel(rightWidgetsModel, order)
saveSettings() saveSettings()
} }
@@ -824,13 +781,13 @@ Singleton {
widgetDataChanged() widgetDataChanged()
} }
function resetDankBarWidgetsToDefault() { function resetTopBarWidgetsToDefault() {
var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"] var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"]
var defaultCenter = ["music", "clock", "weather"] var defaultCenter = ["music", "clock", "weather"]
var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"] var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"]
dankBarLeftWidgets = defaultLeft topBarLeftWidgets = defaultLeft
dankBarCenterWidgets = defaultCenter topBarCenterWidgets = defaultCenter
dankBarRightWidgets = defaultRight topBarRightWidgets = defaultRight
updateListModel(leftWidgetsModel, defaultLeft) updateListModel(leftWidgetsModel, defaultLeft)
updateListModel(centerWidgetsModel, defaultCenter) updateListModel(centerWidgetsModel, defaultCenter)
updateListModel(rightWidgetsModel, defaultRight) updateListModel(rightWidgetsModel, defaultRight)
@@ -997,24 +954,6 @@ Singleton {
function setShowDock(enabled) { function setShowDock(enabled) {
showDock = enabled showDock = enabled
if (enabled && dockPosition === dankBarPosition) {
if (dankBarPosition === SettingsData.Position.Top) {
setDockPosition(SettingsData.Position.Bottom)
return
}
if (dankBarPosition === SettingsData.Position.Bottom) {
setDockPosition(SettingsData.Position.Top)
return
}
if (dankBarPosition === SettingsData.Position.Left) {
setDockPosition(SettingsData.Position.Right)
return
}
if (dankBarPosition === SettingsData.Position.Right) {
setDockPosition(SettingsData.Position.Left)
return
}
}
saveSettings() saveSettings()
} }
@@ -1028,11 +967,6 @@ Singleton {
saveSettings() saveSettings()
} }
function setdockOpenOnOverview(enabled) {
dockOpenOnOverview = enabled
saveSettings()
}
function setCornerRadius(radius) { function setCornerRadius(radius) {
cornerRadius = radius cornerRadius = radius
saveSettings() saveSettings()
@@ -1043,23 +977,23 @@ Singleton {
saveSettings() saveSettings()
} }
function setDankBarAutoHide(enabled) { function setTopBarAutoHide(enabled) {
dankBarAutoHide = enabled topBarAutoHide = enabled
saveSettings() saveSettings()
} }
function setDankBarOpenOnOverview(enabled) { function setTopBarOpenOnOverview(enabled) {
dankBarOpenOnOverview = enabled topBarOpenOnOverview = enabled
saveSettings() saveSettings()
} }
function setDankBarVisible(visible) { function setTopBarVisible(visible) {
dankBarVisible = visible topBarVisible = visible
saveSettings() saveSettings()
} }
function toggleDankBarVisible() { function toggleTopBarVisible() {
dankBarVisible = !dankBarVisible topBarVisible = !topBarVisible
saveSettings() saveSettings()
} }
@@ -1078,162 +1012,36 @@ Singleton {
saveSettings() saveSettings()
} }
function setNotificationPopupPosition(position) { function setTopBarSpacing(spacing) {
notificationPopupPosition = position topBarSpacing = spacing
saveSettings() saveSettings()
} }
function sendTestNotifications() { function setTopBarBottomGap(gap) {
sendTestNotification(0) topBarBottomGap = gap
testNotifTimer1.start()
testNotifTimer2.start()
}
function sendTestNotification(index) {
const notifications = [
["Notification Position Test", "DMS test notification 1 of 3 ~ Hi there!", "dialog-information"],
["Second Test", "DMS Notification 2 of 3 ~ Check it out!", "emblem-default"],
["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "emblem-favorite"]
]
if (index < 0 || index >= notifications.length) {
return
}
const notif = notifications[index]
testNotificationProcess.command = ["notify-send", "-h", "int:transient:1", "-a", "DMS", "-i", notif[2], notif[0], notif[1]]
testNotificationProcess.running = true
}
property Process testNotificationProcess
testNotificationProcess: Process {
command: []
running: false
}
property Timer testNotifTimer1
testNotifTimer1: Timer {
interval: 400
repeat: false
onTriggered: sendTestNotification(1)
}
property Timer testNotifTimer2
testNotifTimer2: Timer {
interval: 800
repeat: false
onTriggered: sendTestNotification(2)
}
function setDankBarSpacing(spacing) {
dankBarSpacing = spacing
saveSettings() saveSettings()
} }
function setDankBarBottomGap(gap) { function setTopBarInnerPadding(padding) {
dankBarBottomGap = gap topBarInnerPadding = padding
saveSettings() saveSettings()
} }
function setDankBarInnerPadding(padding) { function setTopBarSquareCorners(enabled) {
dankBarInnerPadding = padding topBarSquareCorners = enabled
saveSettings() saveSettings()
} }
function setDankBarSquareCorners(enabled) { function setTopBarNoBackground(enabled) {
dankBarSquareCorners = enabled topBarNoBackground = enabled
saveSettings() saveSettings()
} }
function setDankBarNoBackground(enabled) { function setTopBarGothCornersEnabled(enabled) {
dankBarNoBackground = enabled topBarGothCornersEnabled = enabled
saveSettings() saveSettings()
} }
function setDankBarGothCornersEnabled(enabled) {
dankBarGothCornersEnabled = enabled
saveSettings()
}
function setDankBarPosition(position) {
dankBarPosition = position
if (position === SettingsData.Position.Bottom && dockPosition === SettingsData.Position.Bottom && showDock) {
setDockPosition(SettingsData.Position.Top)
return
}
if (position === SettingsData.Position.Top && dockPosition === SettingsData.Position.Top && showDock) {
setDockPosition(SettingsData.Position.Bottom)
return
}
if (position === SettingsData.Position.Left && dockPosition === SettingsData.Position.Left && showDock) {
setDockPosition(SettingsData.Position.Right)
return
}
if (position === SettingsData.Position.Right && dockPosition === SettingsData.Position.Right && showDock) {
setDockPosition(SettingsData.Position.Left)
return
}
saveSettings()
}
function setDockPosition(position) {
dockPosition = position
if (position === SettingsData.Position.Bottom && dankBarPosition === SettingsData.Position.Bottom && showDock) {
setDankBarPosition(SettingsData.Position.Top)
}
if (position === SettingsData.Position.Top && dankBarPosition === SettingsData.Position.Top && showDock) {
setDankBarPosition(SettingsData.Position.Bottom)
}
if (position === SettingsData.Position.Left && dankBarPosition === SettingsData.Position.Left && showDock) {
setDankBarPosition(SettingsData.Position.Right)
}
if (position === SettingsData.Position.Right && dankBarPosition === SettingsData.Position.Right && showDock) {
setDankBarPosition(SettingsData.Position.Left)
}
saveSettings()
Qt.callLater(() => forceDockLayoutRefresh())
}
function setDockSpacing(spacing) {
dockSpacing = spacing
saveSettings()
}
function setDockBottomGap(gap) {
dockBottomGap = gap
saveSettings()
}
function setDockOpenOnOverview(enabled) {
dockOpenOnOverview = enabled
saveSettings()
}
function getPopupYPosition(barHeight) {
const gothOffset = dankBarGothCornersEnabled ? Theme.cornerRadius : 0
return barHeight + dankBarSpacing + dankBarBottomGap - gothOffset + Theme.popupDistance
}
function getPopupTriggerPosition(globalPos, screen, barThickness, widgetWidth) {
const screenX = screen ? screen.x : 0
const screenY = screen ? screen.y : 0
const relativeX = globalPos.x - screenX
const relativeY = globalPos.y - screenY
if (dankBarPosition === SettingsData.Position.Left || dankBarPosition === SettingsData.Position.Right) {
return {
x: relativeY,
y: barThickness + dankBarSpacing + Theme.popupDistance,
width: widgetWidth
}
}
return {
x: relativeX,
y: barThickness + dankBarSpacing + dankBarBottomGap + Theme.popupDistance,
width: widgetWidth
}
}
function setLockScreenShowPowerActions(enabled) { function setLockScreenShowPowerActions(enabled) {
lockScreenShowPowerActions = enabled lockScreenShowPowerActions = enabled
saveSettings() saveSettings()
@@ -1270,48 +1078,14 @@ Singleton {
return Quickshell.screens.filter(screen => prefs.includes(screen.name)) return Quickshell.screens.filter(screen => prefs.includes(screen.name))
} }
// Plugin settings functions
function getPluginSetting(pluginId, key, defaultValue) {
if (!pluginSettings[pluginId]) {
return defaultValue
}
return pluginSettings[pluginId][key] !== undefined ? pluginSettings[pluginId][key] : defaultValue
}
function setPluginSetting(pluginId, key, value) {
if (!pluginSettings[pluginId]) {
pluginSettings[pluginId] = {}
}
pluginSettings[pluginId][key] = value
saveSettings()
}
function removePluginSettings(pluginId) {
if (pluginSettings[pluginId]) {
delete pluginSettings[pluginId]
saveSettings()
}
}
function getPluginSettingsForPlugin(pluginId) {
return pluginSettings[pluginId] || {}
}
function setAnimationSpeed(speed) {
animationSpeed = speed
saveSettings()
}
function _shq(s) { function _shq(s) {
return "'" + String(s).replace(/'/g, "'\\''") + "'" return "'" + String(s).replace(/'/g, "'\\''") + "'"
} }
Component.onCompleted: { Component.onCompleted: {
if (!isGreeterMode) { loadSettings()
loadSettings() fontCheckTimer.start()
fontCheckTimer.start() initializeListModels()
initializeListModels()
}
} }
ListModel { ListModel {
@@ -1352,22 +1126,20 @@ Singleton {
FileView { FileView {
id: settingsFile id: settingsFile
path: isGreeterMode ? "" : StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json" path: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json"
blockLoading: true blockLoading: true
blockWrites: true blockWrites: true
atomicWrites: true atomicWrites: true
watchChanges: !isGreeterMode watchChanges: true
onLoaded: { onLoaded: {
if (!isGreeterMode) { parseSettings(settingsFile.text())
parseSettings(settingsFile.text()) hasTriedDefaultSettings = false
hasTriedDefaultSettings = false
}
} }
onLoadFailed: error => { onLoadFailed: error => {
if (!isGreeterMode && !hasTriedDefaultSettings) { if (!hasTriedDefaultSettings) {
hasTriedDefaultSettings = true hasTriedDefaultSettings = true
defaultSettingsCheckProcess.running = true defaultSettingsCheckProcess.running = true
} else if (!isGreeterMode) { } else {
applyStoredTheme() applyStoredTheme()
} }
} }
@@ -1452,22 +1224,22 @@ Singleton {
IpcHandler { IpcHandler {
function reveal(): string { function reveal(): string {
root.setDankBarVisible(true) root.setTopBarVisible(true)
return "BAR_SHOW_SUCCESS" return "BAR_SHOW_SUCCESS"
} }
function hide(): string { function hide(): string {
root.setDankBarVisible(false) root.setTopBarVisible(false)
return "BAR_HIDE_SUCCESS" return "BAR_HIDE_SUCCESS"
} }
function toggle(): string { function toggle(): string {
root.toggleDankBarVisible() root.toggleTopBarVisible()
return root.dankBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS" return topBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS"
} }
function status(): string { function status(): string {
return root.dankBarVisible ? "visible" : "hidden" return topBarVisible ? "visible" : "hidden"
} }
target: "bar" target: "bar"

View File

@@ -2,101 +2,101 @@
// Separated from Theme.qml to keep that file clean // Separated from Theme.qml to keep that file clean
const CatppuccinMocha = { const CatppuccinMocha = {
surface: "#313244", surface: "#45475a",
surfaceText: "#cdd6f4", surfaceText: "#cdd6f4",
surfaceVariant: "#313244", surfaceVariant: "#45475a",
surfaceVariantText: "#a6adc8", surfaceVariantText: "#a6adc8",
background: "#1e1e2e", background: "#1e1e2e",
backgroundText: "#cdd6f4", backgroundText: "#cdd6f4",
outline: "#6c7086", outline: "#6c7086",
surfaceContainer: "#45475a", surfaceContainer: "#313244",
surfaceContainerHigh: "#585b70", surfaceContainerHigh: "#585b70",
surfaceContainerHighest: "#6c7086" surfaceContainerHighest: "#7f849c"
} }
const CatppuccinLatte = { const CatppuccinLatte = {
surface: "#e6e9ef", surface: "#bcc0cc",
surfaceText: "#4c4f69", surfaceText: "#4c4f69",
surfaceVariant: "#e6e9ef", surfaceVariant: "#bcc0cc",
surfaceVariantText: "#6c6f85", surfaceVariantText: "#6c6f85",
background: "#eff1f5", background: "#eff1f5",
backgroundText: "#4c4f69", backgroundText: "#4c4f69",
outline: "#9ca0b0", outline: "#9ca0b0",
surfaceContainer: "#dce0e8", surfaceContainer: "#ccd0da",
surfaceContainerHigh: "#ccd0da", surfaceContainerHigh: "#acb0be",
surfaceContainerHighest: "#bcc0cc" surfaceContainerHighest: "#8c8fa1"
} }
const CatppuccinVariants = { const CatppuccinVariants = {
"cat-rosewater": { "cat-rosewater": {
name: "Rosewater", name: "Rosewater",
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" }, dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#8b6b5e", surfaceTint: "#f5e0dc" },
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" } light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f4d2ca", surfaceTint: "#dc8a78" }
}, },
"cat-flamingo": { "cat-flamingo": {
name: "Flamingo", name: "Flamingo",
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" }, dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#885d62", surfaceTint: "#f2cdcd" },
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" } light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f4caca", surfaceTint: "#dd7878" }
}, },
"cat-pink": { "cat-pink": {
name: "Pink", name: "Pink",
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" }, dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#8b537a", surfaceTint: "#f5c2e7" },
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" } light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7c9e7", surfaceTint: "#ea76cb" }
}, },
"cat-mauve": { "cat-mauve": {
name: "Mauve", name: "Mauve",
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" }, dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#61378a", surfaceTint: "#cba6f7" },
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" } light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e4d3ff", surfaceTint: "#8839ef" }
}, },
"cat-red": { "cat-red": {
name: "Red", name: "Red",
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" }, dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#891c3b", surfaceTint: "#f38ba8" },
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" } light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f1b8c4", surfaceTint: "#d20f39" }
}, },
"cat-maroon": { "cat-maroon": {
name: "Maroon", name: "Maroon",
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" }, dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#81313f", surfaceTint: "#eba0ac" },
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" } light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f4c3c8", surfaceTint: "#e64553" }
}, },
"cat-peach": { "cat-peach": {
name: "Peach", name: "Peach",
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" }, dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#90441a", surfaceTint: "#fab387" },
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" } light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffddcc", surfaceTint: "#fe640b" }
}, },
"cat-yellow": { "cat-yellow": {
name: "Yellow", name: "Yellow",
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" }, dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#8f7342", surfaceTint: "#f9e2af" },
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" } light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff3cc", surfaceTint: "#df8e1d" }
}, },
"cat-green": { "cat-green": {
name: "Green", name: "Green",
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" }, dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#3c7534", surfaceTint: "#a6e3a1" },
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" } light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#d4f5d4", surfaceTint: "#40a02b" }
}, },
"cat-teal": { "cat-teal": {
name: "Teal", name: "Teal",
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" }, dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2a7468", surfaceTint: "#94e2d5" },
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" } light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#ccf2f2", surfaceTint: "#179299" }
}, },
"cat-sky": { "cat-sky": {
name: "Sky", name: "Sky",
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" }, dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#196e7e", surfaceTint: "#89dceb" },
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" } light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#ccebff", surfaceTint: "#04a5e5" }
}, },
"cat-sapphire": { "cat-sapphire": {
name: "Sapphire", name: "Sapphire",
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" }, dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#0a597f", surfaceTint: "#74c7ec" },
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" } light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#d0f0f5", surfaceTint: "#209fb5" }
}, },
"cat-blue": { "cat-blue": {
name: "Blue", name: "Blue",
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" }, dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#19468d", surfaceTint: "#89b4fa" },
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" } light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#ccd9ff", surfaceTint: "#1e66f5" }
}, },
"cat-lavender": { "cat-lavender": {
name: "Lavender", name: "Lavender",
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" }, dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#4a5091", surfaceTint: "#b4befe" },
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" } light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#dde1ff", surfaceTint: "#7287fd" }
} }
} }
@@ -120,17 +120,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#0d47a1", primaryContainer: "#0d47a1",
secondary: "#8ab4f8", secondary: "#8ab4f8",
surface: "#101418", surface: "#1a1c1e",
surfaceText: "#e0e2e8", surfaceText: "#e3e8ef",
surfaceVariant: "#42474e", surfaceVariant: "#44464f",
surfaceVariantText: "#c2c7cf", surfaceVariantText: "#c4c7c5",
surfaceTint: "#8ab4f8", surfaceTint: "#8ab4f8",
background: "#101418", background: "#1a1c1e",
backgroundText: "#e0e2e8", backgroundText: "#e3e8ef",
outline: "#8c9199", outline: "#8e918f",
surfaceContainer: "#1d2024", surfaceContainer: "#1e2023",
surfaceContainerHigh: "#272a2f", surfaceContainerHigh: "#292b2f",
surfaceContainerHighest: "#32353a" surfaceContainerHighest: "#343740"
}, },
purple: { purple: {
name: "Purple", name: "Purple",
@@ -138,17 +138,17 @@ const StockThemes = {
primaryText: "#381E72", primaryText: "#381E72",
primaryContainer: "#4F378B", primaryContainer: "#4F378B",
secondary: "#CCC2DC", secondary: "#CCC2DC",
surface: "#141218", surface: "#10121E",
surfaceText: "#e6e0e9", surfaceText: "#E6E0E9",
surfaceVariant: "#49454e", surfaceVariant: "#49454F",
surfaceVariantText: "#cac4cf", surfaceVariantText: "#CAC4D0",
surfaceTint: "#D0BCFF", surfaceTint: "#D0BCFF",
background: "#141218", background: "#10121E",
backgroundText: "#e6e0e9", backgroundText: "#E6E0E9",
outline: "#948f99", outline: "#938F99",
surfaceContainer: "#211f24", surfaceContainer: "#1D1B20",
surfaceContainerHigh: "#2b292f", surfaceContainerHigh: "#2B2930",
surfaceContainerHighest: "#36343a" surfaceContainerHighest: "#36343B"
}, },
green: { green: {
name: "Green", name: "Green",
@@ -156,17 +156,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#1b5e20", primaryContainer: "#1b5e20",
secondary: "#81c995", secondary: "#81c995",
surface: "#10140f", surface: "#0f1411",
surfaceText: "#e0e4db", surfaceText: "#e1f5e3",
surfaceVariant: "#424940", surfaceVariant: "#404943",
surfaceVariantText: "#c2c9bd", surfaceVariantText: "#c1cbc4",
surfaceTint: "#81c995", surfaceTint: "#81c995",
background: "#10140f", background: "#0f1411",
backgroundText: "#e0e4db", backgroundText: "#e1f5e3",
outline: "#8c9388", outline: "#8b938c",
surfaceContainer: "#1d211b", surfaceContainer: "#1a1f1b",
surfaceContainerHigh: "#272b25", surfaceContainerHigh: "#252a26",
surfaceContainerHighest: "#323630" surfaceContainerHighest: "#30352f"
}, },
orange: { orange: {
name: "Orange", name: "Orange",
@@ -174,17 +174,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#3e2723", primaryContainer: "#3e2723",
secondary: "#ffb74d", secondary: "#ffb74d",
surface: "#1a120e", surface: "#1c1410",
surfaceText: "#f0dfd8", surfaceText: "#f5f1ea",
surfaceVariant: "#52443d", surfaceVariant: "#4a453a",
surfaceVariantText: "#d7c2b9", surfaceVariantText: "#cbc5b8",
surfaceTint: "#ffb74d", surfaceTint: "#ffb74d",
background: "#1a120e", background: "#1c1410",
backgroundText: "#f0dfd8", backgroundText: "#f5f1ea",
outline: "#a08d85", outline: "#958f84",
surfaceContainer: "#271e1a", surfaceContainer: "#211e17",
surfaceContainerHigh: "#322824", surfaceContainerHigh: "#2c291f",
surfaceContainerHighest: "#3d332e" surfaceContainerHighest: "#373427"
}, },
red: { red: {
name: "Red", name: "Red",
@@ -192,17 +192,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#4a0e0e", primaryContainer: "#4a0e0e",
secondary: "#f28b82", secondary: "#f28b82",
surface: "#1a1110", surface: "#1c1011",
surfaceText: "#f1dedc", surfaceText: "#f5e8ea",
surfaceVariant: "#534341", surfaceVariant: "#4a3f41",
surfaceVariantText: "#d8c2be", surfaceVariantText: "#cbc2c4",
surfaceTint: "#f28b82", surfaceTint: "#f28b82",
background: "#1a1110", background: "#1c1011",
backgroundText: "#f1dedc", backgroundText: "#f5e8ea",
outline: "#a08c89", outline: "#958b8d",
surfaceContainer: "#271d1c", surfaceContainer: "#211b1c",
surfaceContainerHigh: "#322826", surfaceContainerHigh: "#2c2426",
surfaceContainerHighest: "#3d3231" surfaceContainerHighest: "#372f30"
}, },
cyan: { cyan: {
name: "Cyan", name: "Cyan",
@@ -210,15 +210,15 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#004d5c", primaryContainer: "#004d5c",
secondary: "#4dd0e1", secondary: "#4dd0e1",
surface: "#0e1416", surface: "#0f1617",
surfaceText: "#dee3e5", surfaceText: "#e8f4f5",
surfaceVariant: "#3f484a", surfaceVariant: "#3f474a",
surfaceVariantText: "#bfc8ca", surfaceVariantText: "#c2c9cb",
surfaceTint: "#4dd0e1", surfaceTint: "#4dd0e1",
background: "#0e1416", background: "#0f1617",
backgroundText: "#dee3e5", backgroundText: "#e8f4f5",
outline: "#899295", outline: "#8c9194",
surfaceContainer: "#1b2122", surfaceContainer: "#1a1f20",
surfaceContainerHigh: "#252b2c", surfaceContainerHigh: "#252b2c",
surfaceContainerHighest: "#303637" surfaceContainerHighest: "#303637"
}, },
@@ -228,17 +228,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#4a0e2f", primaryContainer: "#4a0e2f",
secondary: "#f8bbd9", secondary: "#f8bbd9",
surface: "#191112", surface: "#1a1014",
surfaceText: "#f0dee0", surfaceText: "#f3e8ee",
surfaceVariant: "#524345", surfaceVariant: "#483f45",
surfaceVariantText: "#d6c2c3", surfaceVariantText: "#c9c2c7",
surfaceTint: "#f8bbd9", surfaceTint: "#f8bbd9",
background: "#191112", background: "#1a1014",
backgroundText: "#f0dee0", backgroundText: "#f3e8ee",
outline: "#9f8c8e", outline: "#938a90",
surfaceContainer: "#261d1e", surfaceContainer: "#1f1b1e",
surfaceContainerHigh: "#312829", surfaceContainerHigh: "#2a2428",
surfaceContainerHighest: "#3c3233" surfaceContainerHighest: "#352f32"
}, },
amber: { amber: {
name: "Amber", name: "Amber",
@@ -246,17 +246,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#4a3c00", primaryContainer: "#4a3c00",
secondary: "#ffd54f", secondary: "#ffd54f",
surface: "#17130b", surface: "#1a1710",
surfaceText: "#ebe1d4", surfaceText: "#f3f0e8",
surfaceVariant: "#4d4639", surfaceVariant: "#49453a",
surfaceVariantText: "#d0c5b4", surfaceVariantText: "#cac5b8",
surfaceTint: "#ffd54f", surfaceTint: "#ffd54f",
background: "#17130b", background: "#1a1710",
backgroundText: "#ebe1d4", backgroundText: "#f3f0e8",
outline: "#998f80", outline: "#949084",
surfaceContainer: "#231f17", surfaceContainer: "#1f1e17",
surfaceContainerHigh: "#2e2921", surfaceContainerHigh: "#2a281f",
surfaceContainerHighest: "#39342b" surfaceContainerHighest: "#353327"
}, },
coral: { coral: {
name: "Coral", name: "Coral",
@@ -265,16 +265,16 @@ const StockThemes = {
primaryContainer: "#8c1d18", primaryContainer: "#8c1d18",
secondary: "#f9dedc", secondary: "#f9dedc",
surface: "#1a1110", surface: "#1a1110",
surfaceText: "#f1dedc", surfaceText: "#f1e8e7",
surfaceVariant: "#534341", surfaceVariant: "#4a4142",
surfaceVariantText: "#d8c2bf", surfaceVariantText: "#cdc2c1",
surfaceTint: "#ffb4ab", surfaceTint: "#ffb4ab",
background: "#1a1110", background: "#1a1110",
backgroundText: "#f1dedc", backgroundText: "#f1e8e7",
outline: "#a08c8a", outline: "#968b8a",
surfaceContainer: "#271d1c", surfaceContainer: "#201a19",
surfaceContainerHigh: "#322826", surfaceContainerHigh: "#2b2221",
surfaceContainerHighest: "#3d3231" surfaceContainerHighest: "#362d29"
}, },
monochrome: { monochrome: {
name: "Monochrome", name: "Monochrome",
@@ -290,9 +290,9 @@ const StockThemes = {
background: "#131315", background: "#131315",
backgroundText: "#e4e2e3", backgroundText: "#e4e2e3",
outline: "#929092", outline: "#929092",
surfaceContainer: "#353535", surfaceContainer: "#2a2a2a",
surfaceContainerHigh: "#424242", surfaceContainerHigh: "#2a2a2b",
surfaceContainerHighest: "#505050", surfaceContainerHighest: "#353535",
error: "#ffb4ab", error: "#ffb4ab",
warning: "#3f4759", warning: "#3f4759",
info: "#595e6c", info: "#595e6c",
@@ -306,17 +306,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#e3f2fd", primaryContainer: "#e3f2fd",
secondary: "#42a5f5", secondary: "#42a5f5",
surface: "#f7f9ff", surface: "#fefefe",
surfaceText: "#181c20", surfaceText: "#1a1c1e",
surfaceVariant: "#dee3eb", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#42474e", surfaceVariantText: "#49454f",
surfaceTint: "#1976d2", surfaceTint: "#1976d2",
background: "#f7f9ff", background: "#fefefe",
backgroundText: "#181c20", backgroundText: "#1a1c1e",
outline: "#72777f", outline: "#79747e",
surfaceContainer: "#eceef4", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#e6e8ee", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#e0e2e8" surfaceContainerHighest: "#e6e6e6"
}, },
purple: { purple: {
name: "Purple Light", name: "Purple Light",
@@ -324,17 +324,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#EADDFF", primaryContainer: "#EADDFF",
secondary: "#625B71", secondary: "#625B71",
surface: "#fef7ff", surface: "#FFFBFE",
surfaceText: "#1d1b20", surfaceText: "#1C1B1F",
surfaceVariant: "#e7e0eb", surfaceVariant: "#E7E0EC",
surfaceVariantText: "#49454e", surfaceVariantText: "#49454F",
surfaceTint: "#6750A4", surfaceTint: "#6750A4",
background: "#fef7ff", background: "#FFFBFE",
backgroundText: "#1d1b20", backgroundText: "#1C1B1F",
outline: "#7a757f", outline: "#79747E",
surfaceContainer: "#f2ecf4", surfaceContainer: "#F3EDF7",
surfaceContainerHigh: "#ece6ee", surfaceContainerHigh: "#ECE6F0",
surfaceContainerHighest: "#e6e0e9" surfaceContainerHighest: "#E6DFE9"
}, },
green: { green: {
name: "Green Light", name: "Green Light",
@@ -342,17 +342,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#e8f5e8", primaryContainer: "#e8f5e8",
secondary: "#4caf50", secondary: "#4caf50",
surface: "#f7fbf1", surface: "#fefefe",
surfaceText: "#191d17", surfaceText: "#1a1c1e",
surfaceVariant: "#dee5d8", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#424940", surfaceVariantText: "#49454f",
surfaceTint: "#2e7d32", surfaceTint: "#2e7d32",
background: "#f7fbf1", background: "#fefefe",
backgroundText: "#191d17", backgroundText: "#1a1c1e",
outline: "#72796f", outline: "#79747e",
surfaceContainer: "#ecefe6", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#e6e9e0", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#e0e4db" surfaceContainerHighest: "#e6e6e6"
}, },
orange: { orange: {
name: "Orange Light", name: "Orange Light",
@@ -360,17 +360,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#ffecb3", primaryContainer: "#ffecb3",
secondary: "#ff9800", secondary: "#ff9800",
surface: "#fff8f6", surface: "#fefefe",
surfaceText: "#221a16", surfaceText: "#1a1c1e",
surfaceVariant: "#f4ded5", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#52443d", surfaceVariantText: "#49454f",
surfaceTint: "#e65100", surfaceTint: "#e65100",
background: "#fff8f6", background: "#fefefe",
backgroundText: "#221a16", backgroundText: "#1a1c1e",
outline: "#85736c", outline: "#79747e",
surfaceContainer: "#fceae3", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#f6e5de", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#f0dfd8" surfaceContainerHighest: "#e6e6e6"
}, },
red: { red: {
name: "Red Light", name: "Red Light",
@@ -378,17 +378,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#ffebee", primaryContainer: "#ffebee",
secondary: "#f44336", secondary: "#f44336",
surface: "#fff8f7", surface: "#fefefe",
surfaceText: "#231918", surfaceText: "#1a1c1e",
surfaceVariant: "#f5ddda", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#534341", surfaceVariantText: "#49454f",
surfaceTint: "#d32f2f", surfaceTint: "#d32f2f",
background: "#fff8f7", background: "#fefefe",
backgroundText: "#231918", backgroundText: "#1a1c1e",
outline: "#857370", outline: "#79747e",
surfaceContainer: "#fceae7", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#f7e4e1", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#f1dedc" surfaceContainerHighest: "#e6e6e6"
}, },
cyan: { cyan: {
name: "Cyan Light", name: "Cyan Light",
@@ -396,17 +396,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#e0f2f1", primaryContainer: "#e0f2f1",
secondary: "#00bcd4", secondary: "#00bcd4",
surface: "#f5fafc", surface: "#fefefe",
surfaceText: "#171d1e", surfaceText: "#1a1c1e",
surfaceVariant: "#dbe4e6", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#3f484a", surfaceVariantText: "#49454f",
surfaceTint: "#0097a7", surfaceTint: "#0097a7",
background: "#f5fafc", background: "#fefefe",
backgroundText: "#171d1e", backgroundText: "#1a1c1e",
outline: "#6f797b", outline: "#79747e",
surfaceContainer: "#e9eff0", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#e3e9eb", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#dee3e5" surfaceContainerHighest: "#e6e6e6"
}, },
pink: { pink: {
name: "Pink Light", name: "Pink Light",
@@ -414,17 +414,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#fce4ec", primaryContainer: "#fce4ec",
secondary: "#e91e63", secondary: "#e91e63",
surface: "#fff8f7", surface: "#fefefe",
surfaceText: "#22191a", surfaceText: "#1a1c1e",
surfaceVariant: "#f3dddf", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#524345", surfaceVariantText: "#49454f",
surfaceTint: "#c2185b", surfaceTint: "#c2185b",
background: "#fff8f7", background: "#fefefe",
backgroundText: "#22191a", backgroundText: "#1a1c1e",
outline: "#847375", outline: "#79747e",
surfaceContainer: "#fbeaeb", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#f5e4e5", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#f0dee0" surfaceContainerHighest: "#e6e6e6"
}, },
amber: { amber: {
name: "Amber Light", name: "Amber Light",
@@ -432,17 +432,17 @@ const StockThemes = {
primaryText: "#000000", primaryText: "#000000",
primaryContainer: "#fff8e1", primaryContainer: "#fff8e1",
secondary: "#ffc107", secondary: "#ffc107",
surface: "#fff8f2", surface: "#fefefe",
surfaceText: "#1f1b13", surfaceText: "#1a1c1e",
surfaceVariant: "#ede1cf", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#4d4639", surfaceVariantText: "#49454f",
surfaceTint: "#ff8f00", surfaceTint: "#ff8f00",
background: "#fff8f2", background: "#fefefe",
backgroundText: "#1f1b13", backgroundText: "#1a1c1e",
outline: "#7f7667", outline: "#79747e",
surfaceContainer: "#f6ecdf", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#f1e7d9", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#ebe1d4" surfaceContainerHighest: "#e6e6e6"
}, },
coral: { coral: {
name: "Coral Light", name: "Coral Light",
@@ -450,17 +450,17 @@ const StockThemes = {
primaryText: "#ffffff", primaryText: "#ffffff",
primaryContainer: "#ffdad6", primaryContainer: "#ffdad6",
secondary: "#ff5449", secondary: "#ff5449",
surface: "#fff8f7", surface: "#fefefe",
surfaceText: "#231918", surfaceText: "#1a1c1e",
surfaceVariant: "#f5ddda", surfaceVariant: "#e7e0ec",
surfaceVariantText: "#534341", surfaceVariantText: "#49454f",
surfaceTint: "#8c1d18", surfaceTint: "#8c1d18",
background: "#fff8f7", background: "#fefefe",
backgroundText: "#231918", backgroundText: "#1a1c1e",
outline: "#857371", outline: "#79747e",
surfaceContainer: "#fceae7", surfaceContainer: "#f3f3f3",
surfaceContainerHigh: "#f6e4e2", surfaceContainerHigh: "#ececec",
surfaceContainerHighest: "#f1dedc" surfaceContainerHighest: "#e6e6e6"
}, },
monochrome: { monochrome: {
name: "Monochrome Light", name: "Monochrome Light",
@@ -476,9 +476,8 @@ const StockThemes = {
background: "#ffffff", background: "#ffffff",
backgroundText: "#1a1a1a", backgroundText: "#1a1a1a",
outline: "#757577", outline: "#757577",
surfaceContainer: "#e8e8ea", surfaceContainer: "#f5f5f6",
surfaceContainerHigh: "#dcdcde", surfaceContainerHigh: "#eaeaeb",
surfaceContainerHighest: "#d0d0d2",
error: "#ba1a1a", error: "#ba1a1a",
warning: "#f9e79f", warning: "#f9e79f",
info: "#5d6475", info: "#5d6475",

View File

@@ -16,12 +16,9 @@ Singleton {
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true" readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
// ! TODO - Synchronize with niri/hyprland gaps?
readonly property real popupDistance: 2
property string currentTheme: "blue" property string currentTheme: "blue"
property string currentThemeCategory: "generic" property string currentThemeCategory: "generic"
property bool isLightMode: typeof SessionData !== "undefined" ? SessionData.isLightMode : false property bool isLightMode: false
readonly property string dynamic: "dynamic" readonly property string dynamic: "dynamic"
readonly property string custom : "custom" readonly property string custom : "custom"
@@ -31,8 +28,9 @@ Singleton {
readonly property string shellDir: Paths.strip(Qt.resolvedUrl(".").toString()).replace("/Common/", "") readonly property string shellDir: Paths.strip(Qt.resolvedUrl(".").toString()).replace("/Common/", "")
readonly property string wallpaperPath: { readonly property string wallpaperPath: {
if (typeof SessionData === "undefined") return "" if (typeof SessionData === "undefined") return ""
if (SessionData.perMonitorWallpaper) { if (SessionData.perMonitorWallpaper) {
// Use first monitor's wallpaper for dynamic theming
var screens = Quickshell.screens var screens = Quickshell.screens
if (screens.length > 0) { if (screens.length > 0) {
var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name) var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name)
@@ -74,6 +72,7 @@ Singleton {
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
property var workerRunning: false property var workerRunning: false
property var matugenColors: ({}) property var matugenColors: ({})
property bool extractionRequested: false
property int colorUpdateTrigger: 0 property int colorUpdateTrigger: 0
property var customThemeData: null property var customThemeData: null
@@ -82,20 +81,11 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", stateDir]) Quickshell.execDetached(["mkdir", "-p", stateDir])
matugenCheck.running = true matugenCheck.running = true
if (typeof SessionData !== "undefined") { if (typeof SessionData !== "undefined")
SessionData.isLightModeChanged.connect(root.onLightModeChanged) SessionData.isLightModeChanged.connect(root.onLightModeChanged)
isLightMode = SessionData.isLightMode
}
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) { if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
switchTheme(SettingsData.currentThemeName, false, false) switchTheme(SettingsData.currentThemeName, false)
}
}
function applyGreeterTheme(themeName) {
switchTheme(themeName, false, false)
if (themeName === dynamic && dynamicColorsFileView.path) {
dynamicColorsFileView.reload()
} }
} }
@@ -241,22 +231,11 @@ Singleton {
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08) property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3) property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
readonly property var animationDurations: [ property int shorterDuration: 100
{ shorter: 0, short: 0, medium: 0, long: 0, extraLong: 0 }, property int shortDuration: 150
{ shorter: 50, short: 75, medium: 150, long: 250, extraLong: 500 }, property int mediumDuration: 300
{ shorter: 100, short: 150, medium: 300, long: 500, extraLong: 1000 }, property int longDuration: 500
{ shorter: 150, short: 225, medium: 450, long: 750, extraLong: 1500 }, property int extraLongDuration: 1000
{ shorter: 200, short: 300, medium: 600, long: 1000, extraLong: 2000 }
]
readonly property int currentAnimationSpeed: typeof SettingsData !== "undefined" ? SettingsData.animationSpeed : SettingsData.AnimationSpeed.Short
readonly property var currentDurations: animationDurations[currentAnimationSpeed] || animationDurations[SettingsData.AnimationSpeed.Short]
property int shorterDuration: currentDurations.shorter
property int shortDuration: currentDurations.short
property int mediumDuration: currentDurations.medium
property int longDuration: currentDurations.long
property int extraLongDuration: currentDurations.extraLong
property int standardEasing: Easing.OutCubic property int standardEasing: Easing.OutCubic
property int emphasizedEasing: Easing.OutQuart property int emphasizedEasing: Easing.OutQuart
@@ -276,8 +255,8 @@ Singleton {
property real iconSizeLarge: 32 property real iconSizeLarge: 32
property real panelTransparency: 0.85 property real panelTransparency: 0.85
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.dankBarWidgetTransparency !== undefined ? SettingsData.dankBarWidgetTransparency : 1.0 property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.topBarWidgetTransparency !== undefined ? SettingsData.topBarWidgetTransparency : 0.85
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0 property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 0.92
function screenTransition() { function screenTransition() {
CompositorService.isNiri && NiriService.doScreenTransition() CompositorService.isNiri && NiriService.doScreenTransition()
@@ -286,15 +265,11 @@ Singleton {
function switchTheme(themeName, savePrefs = true, enableTransition = true) { function switchTheme(themeName, savePrefs = true, enableTransition = true) {
if (enableTransition) { if (enableTransition) {
screenTransition() screenTransition()
themeTransitionTimer.themeName = themeName
themeTransitionTimer.savePrefs = savePrefs
themeTransitionTimer.restart()
return
} }
if (themeName === dynamic) { if (themeName === dynamic) {
currentTheme = dynamic currentTheme = dynamic
currentThemeCategory = dynamic currentThemeCategory = dynamic
extractColors()
} else if (themeName === custom) { } else if (themeName === custom) {
currentTheme = custom currentTheme = custom
currentThemeCategory = custom currentThemeCategory = custom
@@ -303,45 +278,34 @@ Singleton {
} }
} else { } else {
currentTheme = themeName currentTheme = themeName
// Determine category based on theme name
if (StockThemes.isCatppuccinVariant(themeName)) { if (StockThemes.isCatppuccinVariant(themeName)) {
currentThemeCategory = "catppuccin" currentThemeCategory = "catppuccin"
} else { } else {
currentThemeCategory = "generic" currentThemeCategory = "generic"
} }
} }
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode) if (savePrefs && typeof SettingsData !== "undefined")
if (savePrefs && typeof SettingsData !== "undefined" && !isGreeterMode)
SettingsData.setTheme(currentTheme) SettingsData.setTheme(currentTheme)
if (!isGreeterMode) { generateSystemThemesFromCurrentTheme()
generateSystemThemesFromCurrentTheme()
}
} }
function setLightMode(light, savePrefs = true, enableTransition = false) { function setLightMode(light, savePrefs = true) {
if (enableTransition) { screenTransition()
screenTransition()
lightModeTransitionTimer.lightMode = light
lightModeTransitionTimer.savePrefs = savePrefs
lightModeTransitionTimer.restart()
return
}
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode)
isLightMode = light isLightMode = light
if (savePrefs && typeof SessionData !== "undefined" && !isGreeterMode) if (savePrefs && typeof SessionData !== "undefined")
SessionData.setLightMode(isLightMode) SessionData.setLightMode(isLightMode)
if (!isGreeterMode) { PortalService.setLightMode(isLightMode)
PortalService.setLightMode(isLightMode) generateSystemThemesFromCurrentTheme()
generateSystemThemesFromCurrentTheme()
}
} }
function toggleLightMode(savePrefs = true) { function toggleLightMode(savePrefs = true) {
setLightMode(!isLightMode, savePrefs, true) setLightMode(!isLightMode, savePrefs)
} }
function forceGenerateSystemThemes() { function forceGenerateSystemThemes() {
screenTransition()
if (!matugenAvailable) { if (!matugenAvailable) {
return return
} }
@@ -365,10 +329,8 @@ Singleton {
} }
function switchThemeCategory(category, defaultTheme) { function switchThemeCategory(category, defaultTheme) {
screenTransition() currentThemeCategory = category
themeCategoryTransitionTimer.category = category switchTheme(defaultTheme, true, false)
themeCategoryTransitionTimer.defaultTheme = defaultTheme
themeCategoryTransitionTimer.restart()
} }
function getCatppuccinColor(variantName) { function getCatppuccinColor(variantName) {
@@ -392,6 +354,7 @@ Singleton {
} }
function loadCustomTheme(themeData) { function loadCustomTheme(themeData) {
screenTransition()
if (themeData.dark || themeData.light) { if (themeData.dark || themeData.light) {
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark" const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light
@@ -563,6 +526,17 @@ Singleton {
} }
} }
function extractColors() {
extractionRequested = true
if (matugenAvailable)
if (rawWallpaperPath.startsWith("we:")) {
fileCheckerTimer.start()
} else {
fileChecker.running = true
}
else
matugenCheck.running = true
}
function onLightModeChanged() { function onLightModeChanged() {
if (matugenColors && Object.keys(matugenColors).length > 0) { if (matugenColors && Object.keys(matugenColors).length > 0) {
@@ -611,8 +585,7 @@ Singleton {
} }
function generateSystemThemesFromCurrentTheme() { function generateSystemThemesFromCurrentTheme() {
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode) if (!matugenAvailable)
if (!matugenAvailable || isGreeterMode)
return return
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
@@ -676,10 +649,60 @@ Singleton {
qtApplier.running = true qtApplier.running = true
} }
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); } function extractJsonFromText(text) {
if (!text)
return null
function snap(value, dpr) { const start = text.search(/[{\[]/)
return Math.round(value * dpr) / dpr if (start === -1)
return null
const open = text[start]
const pairs = {
"{": '}',
"[": ']'
}
const close = pairs[open]
if (!close)
return null
let inString = false
let escape = false
const stack = [open]
for (var i = start + 1; i < text.length; i++) {
const ch = text[i]
if (inString) {
if (escape) {
escape = false
} else if (ch === '\\') {
escape = true
} else if (ch === '"') {
inString = false
}
continue
}
if (ch === '"') {
inString = true
continue
}
if (ch === '{' || ch === '[') {
stack.push(ch)
continue
}
if (ch === '}' || ch === ']') {
const last = stack.pop()
if (!last || pairs[last] !== ch) {
return null
}
if (stack.length === 0) {
return text.slice(start, i + 1)
}
}
}
return null
} }
Process { Process {
@@ -687,11 +710,17 @@ Singleton {
command: ["which", "matugen"] command: ["which", "matugen"]
onExited: code => { onExited: code => {
matugenAvailable = (code === 0) && !envDisableMatugen matugenAvailable = (code === 0) && !envDisableMatugen
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode) if (!matugenAvailable) {
console.log("matugen not not available in path or disabled via DMS_DISABLE_MATUGEN")
if (!matugenAvailable || isGreeterMode) {
return return
} }
if (extractionRequested) {
if (rawWallpaperPath.startsWith("we:")) {
fileCheckerTimer.start()
} else {
fileChecker.running = true
}
}
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default" const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
@@ -727,7 +756,128 @@ Singleton {
} }
} }
Process {
id: fileChecker
command: ["test", "-r", wallpaperPath]
onExited: code => {
if (code === 0) {
matugenProcess.running = true
} else if (wallpaperPath.startsWith("#")) {
colorMatugenProcess.running = true
}
}
}
Timer {
id: fileCheckerTimer
interval: 1000
repeat: false
onTriggered: {
fileChecker.running = true
}
}
Process {
id: matugenProcess
command: {
const scheme = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
return ["matugen", "image", wallpaperPath, "--json", "hex", "-t", scheme]
}
stdout: StdioCollector {
id: matugenCollector
onStreamFinished: {
if (!matugenCollector.text) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Wallpaper Processing Failed: Empty JSON extracted from matugen output.")
}
return
}
const extractedJson = extractJsonFromText(matugenCollector.text)
if (!extractedJson) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Wallpaper Processing Failed: Invalid JSON extracted from matugen output.")
}
console.log("Raw matugen output:", matugenCollector.text)
return
}
try {
root.matugenColors = JSON.parse(extractedJson)
root.colorUpdateTrigger++
if (typeof ToastService !== "undefined") {
ToastService.clearWallpaperError()
}
} catch (e) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Wallpaper processing failed (JSON parse error after extraction)")
}
}
}
}
onExited: code => {
if (code !== 0) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Matugen command failed with exit code " + code)
}
}
}
}
Process {
id: colorMatugenProcess
command: {
const scheme = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
return ["matugen", "color", "hex", wallpaperPath, "--json", "hex", "-t", scheme]
}
stdout: StdioCollector {
id: colorMatugenCollector
onStreamFinished: {
if (!colorMatugenCollector.text) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Color Processing Failed: Empty JSON extracted from matugen output.")
}
return
}
const extractedJson = extractJsonFromText(colorMatugenCollector.text)
if (!extractedJson) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Color Processing Failed: Invalid JSON extracted from matugen output.")
}
console.log("Raw matugen output:", colorMatugenCollector.text)
return
}
try {
root.matugenColors = JSON.parse(extractedJson)
root.colorUpdateTrigger++
if (typeof ToastService !== "undefined") {
ToastService.clearWallpaperError()
}
} catch (e) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Color processing failed (JSON parse error after extraction)")
}
}
}
}
onExited: code => {
if (code !== 0) {
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Matugen color command failed with exit code " + code)
}
}
}
}
Process { Process {
id: ensureStateDir id: ensureStateDir
@@ -740,7 +890,10 @@ Singleton {
onExited: exitCode => { onExited: exitCode => {
workerRunning = false workerRunning = false
if (exitCode !== 0 && exitCode !== 2) { if (exitCode === 2) {
// Exit code 2 means wallpaper/color not found - this is expected on first run
console.log("Theme worker: wallpaper/color not found, skipping theme generation")
} else if (exitCode !== 0) {
if (typeof ToastService !== "undefined") { if (typeof ToastService !== "undefined") {
ToastService.showError("Theme worker failed (" + exitCode + ")") ToastService.showError("Theme worker failed (" + exitCode + ")")
} }
@@ -827,55 +980,6 @@ Singleton {
} }
} }
FileView {
id: dynamicColorsFileView
path: {
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
const colorsPath = SessionData.isGreeterMode
? greetCfgDir + "/colors.json"
: stateDir + "/dms-colors.json"
return colorsPath
}
watchChanges: currentTheme === dynamic && !SessionData.isGreeterMode
function parseAndLoadColors() {
try {
const colorsText = dynamicColorsFileView.text()
if (colorsText) {
root.matugenColors = JSON.parse(colorsText)
root.colorUpdateTrigger++
if (typeof ToastService !== "undefined") {
ToastService.clearWallpaperError()
}
}
} catch (e) {
console.error("Theme: Failed to parse dynamic colors:", e)
if (typeof ToastService !== "undefined") {
ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Dynamic colors parse error: " + e.message)
}
}
}
onLoaded: {
if (currentTheme === dynamic) {
parseAndLoadColors()
}
}
onFileChanged: {
if (currentTheme === dynamic) {
dynamicColorsFileView.reload()
}
}
onLoadFailed: function (error) {
if (currentTheme === dynamic && typeof ToastService !== "undefined") {
ToastService.showError("Failed to read dynamic colors: " + error)
}
}
}
IpcHandler { IpcHandler {
target: "theme" target: "theme"
@@ -885,12 +989,12 @@ Singleton {
} }
function light(): string { function light(): string {
root.setLightMode(true, true, true) root.setLightMode(true)
return "light" return "light"
} }
function dark(): string { function dark(): string {
root.setLightMode(false, true, true) root.setLightMode(false)
return "dark" return "dark"
} }
@@ -898,35 +1002,4 @@ Singleton {
return root.isLightMode ? "light" : "dark" return root.isLightMode ? "light" : "dark"
} }
} }
// These timers are for screen transitions, since sometimes QML still beats the niri call
Timer {
id: themeTransitionTimer
interval: 50
repeat: false
property string themeName: ""
property bool savePrefs: true
onTriggered: root.switchTheme(themeName, savePrefs, false)
}
Timer {
id: lightModeTransitionTimer
interval: 100
repeat: false
property bool lightMode: false
property bool savePrefs: true
onTriggered: root.setLightMode(lightMode, savePrefs, false)
}
Timer {
id: themeCategoryTransitionTimer
interval: 50
repeat: false
property string category: ""
property string defaultTheme: ""
onTriggered: {
root.currentThemeCategory = category
root.switchTheme(defaultTheme, true, false)
}
}
} }

View File

@@ -1,29 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Greetd
import qs.Common
import qs.Modules.Greetd
ShellRoot {
id: root
WlSessionLock {
id: sessionLock
locked: false
Component.onCompleted: {
Qt.callLater(() => { locked = true })
}
onLockedChanged: {
if (!locked) {
console.log("Greetd session unlocked, exiting")
}
}
GreeterSurface {
lock: sessionLock
}
}
}

View File

@@ -1,628 +0,0 @@
//@ pragma Env QSG_RENDER_LOOP=threaded
//@ pragma UseQApplication
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Modals
import qs.Modals.Clipboard
import qs.Modals.Common
import qs.Modals.Settings
import qs.Modals.Spotlight
import qs.Modules
import qs.Modules.AppDrawer
import qs.Modules.DankDash
import qs.Modules.ControlCenter
import qs.Modules.Dock
import qs.Modules.Lock
import qs.Modules.Notepad
import qs.Modules.Notifications.Center
import qs.Widgets
import qs.Modules.Notifications.Popup
import qs.Modules.OSD
import qs.Modules.ProcessList
import qs.Modules.Settings
import qs.Modules.DankBar
import qs.Modules.DankBar.Popouts
import qs.Modules.Plugins
import qs.Services
ShellRoot {
id: root
Component.onCompleted: {
PortalService.init()
// Initialize DisplayService night mode functionality
DisplayService.nightModeEnabled
// Initialize WallpaperCyclingService
WallpaperCyclingService.cyclingActive
// Initialize PluginService by accessing its properties
PluginService.pluginDirectory
}
WallpaperBackground {}
Lock {
id: lock
anchors.fill: parent
}
Loader {
id: dankBarLoader
asynchronous: false
property var currentPosition: SettingsData.dankBarPosition
sourceComponent: DankBar {
onColorPickerRequested: colorPickerModal.show()
}
onCurrentPositionChanged: {
const component = sourceComponent
sourceComponent = null
Qt.callLater(() => {
sourceComponent = component
})
}
}
Loader {
id: dockLoader
active: true
asynchronous: false
property var currentPosition: SettingsData.dockPosition
sourceComponent: Dock {
contextMenu: dockContextMenuLoader.item ? dockContextMenuLoader.item : null
}
onLoaded: {
if (item) {
dockContextMenuLoader.active = true
}
}
onCurrentPositionChanged: {
console.log("DEBUG: Dock position changed to:", currentPosition, "- recreating dock")
const comp = sourceComponent
sourceComponent = null
Qt.callLater(() => {
sourceComponent = comp
})
}
}
Loader {
id: dankDashPopoutLoader
active: false
asynchronous: true
sourceComponent: Component {
DankDashPopout {
id: dankDashPopout
}
}
}
LazyLoader {
id: dockContextMenuLoader
active: false
DockContextMenu {
id: dockContextMenu
}
}
LazyLoader {
id: notificationCenterLoader
active: false
NotificationCenterPopout {
id: notificationCenter
}
}
Variants {
model: SettingsData.getFilteredScreens("notifications")
delegate: NotificationPopupManager {
modelData: item
}
}
LazyLoader {
id: controlCenterLoader
active: false
property var modalRef: colorPickerModal
property LazyLoader powerModalLoaderRef: powerMenuModalLoader
ControlCenterPopout {
id: controlCenterPopout
colorPickerModal: controlCenterLoader.modalRef
powerMenuModalLoader: controlCenterLoader.powerModalLoaderRef
onLockRequested: {
lock.activate()
}
}
}
LazyLoader {
id: wifiPasswordModalLoader
active: false
WifiPasswordModal {
id: wifiPasswordModal
}
}
LazyLoader {
id: networkInfoModalLoader
active: false
NetworkInfoModal {
id: networkInfoModal
}
}
LazyLoader {
id: batteryPopoutLoader
active: false
BatteryPopout {
id: batteryPopout
}
}
LazyLoader {
id: vpnPopoutLoader
active: false
VpnPopout {
id: vpnPopout
}
}
LazyLoader {
id: powerMenuLoader
active: false
PowerMenu {
id: powerMenu
onPowerActionRequested: (action, title, message) => {
powerConfirmModalLoader.active = true
if (powerConfirmModalLoader.item) {
powerConfirmModalLoader.item.confirmButtonColor = action === "poweroff" ? Theme.error : action === "reboot" ? Theme.warning : Theme.primary
powerConfirmModalLoader.item.show(title, message, function () {
switch (action) {
case "logout":
SessionService.logout()
break
case "suspend":
SessionService.suspend()
break
case "hibernate":
SessionService.hibernate()
break
case "reboot":
SessionService.reboot()
break
case "poweroff":
SessionService.poweroff()
break
}
}, function () {})
}
}
}
}
LazyLoader {
id: powerConfirmModalLoader
active: false
ConfirmModal {
id: powerConfirmModal
}
}
LazyLoader {
id: processListPopoutLoader
active: false
ProcessListPopout {
id: processListPopout
}
}
SettingsModal {
id: settingsModal
}
LazyLoader {
id: appDrawerLoader
active: false
AppDrawerPopout {
id: appDrawerPopout
}
}
SpotlightModal {
id: spotlightModal
}
ClipboardHistoryModal {
id: clipboardHistoryModalPopup
}
NotificationModal {
id: notificationModal
}
ColorPickerModal {
id: colorPickerModal
}
LazyLoader {
id: processListModalLoader
active: false
ProcessListModal {
id: processListModal
}
}
LazyLoader {
id: systemUpdateLoader
active: false
SystemUpdatePopout {
id: systemUpdatePopout
}
}
Variants {
id: notepadSlideoutVariants
model: SettingsData.getFilteredScreens("notepad")
delegate: DankSlideout {
id: notepadSlideout
modelData: item
title: qsTr("Notepad")
slideoutWidth: 480
expandable: true
expandedWidthValue: 960
customTransparency: SettingsData.notepadTransparencyOverride
content: Component {
Notepad {
onHideRequested: {
notepadSlideout.hide()
}
}
}
function toggle() {
if (isVisible) {
hide()
} else {
show()
}
}
}
}
LazyLoader {
id: powerMenuModalLoader
active: false
PowerMenuModal {
id: powerMenuModal
onPowerActionRequested: (action, title, message) => {
powerConfirmModalLoader.active = true
if (powerConfirmModalLoader.item) {
powerConfirmModalLoader.item.confirmButtonColor = action === "poweroff" ? Theme.error : action === "reboot" ? Theme.warning : Theme.primary
powerConfirmModalLoader.item.show(title, message, function () {
switch (action) {
case "logout":
SessionService.logout()
break
case "suspend":
SessionService.suspend()
break
case "hibernate":
SessionService.hibernate()
break
case "reboot":
SessionService.reboot()
break
case "poweroff":
SessionService.poweroff()
break
}
}, function () {})
}
}
}
}
IpcHandler {
function open() {
powerMenuModalLoader.active = true
if (powerMenuModalLoader.item)
powerMenuModalLoader.item.openCentered()
return "POWERMENU_OPEN_SUCCESS"
}
function close() {
if (powerMenuModalLoader.item)
powerMenuModalLoader.item.close()
return "POWERMENU_CLOSE_SUCCESS"
}
function toggle() {
powerMenuModalLoader.active = true
if (powerMenuModalLoader.item) {
if (powerMenuModalLoader.item.shouldBeVisible) {
powerMenuModalLoader.item.close()
} else {
powerMenuModalLoader.item.openCentered()
}
}
return "POWERMENU_TOGGLE_SUCCESS"
}
target: "powermenu"
}
IpcHandler {
function open(): string {
processListModalLoader.active = true
if (processListModalLoader.item)
processListModalLoader.item.show()
return "PROCESSLIST_OPEN_SUCCESS"
}
function close(): string {
if (processListModalLoader.item)
processListModalLoader.item.hide()
return "PROCESSLIST_CLOSE_SUCCESS"
}
function toggle(): string {
processListModalLoader.active = true
if (processListModalLoader.item)
processListModalLoader.item.toggle()
return "PROCESSLIST_TOGGLE_SUCCESS"
}
target: "processlist"
}
IpcHandler {
function open(): string {
controlCenterLoader.active = true
if (controlCenterLoader.item) {
controlCenterLoader.item.open()
return "CONTROL_CENTER_OPEN_SUCCESS"
}
return "CONTROL_CENTER_OPEN_FAILED"
}
function close(): string {
if (controlCenterLoader.item) {
controlCenterLoader.item.close()
return "CONTROL_CENTER_CLOSE_SUCCESS"
}
return "CONTROL_CENTER_CLOSE_FAILED"
}
function toggle(): string {
controlCenterLoader.active = true
if (controlCenterLoader.item) {
controlCenterLoader.item.toggle()
return "CONTROL_CENTER_TOGGLE_SUCCESS"
}
return "CONTROL_CENTER_TOGGLE_FAILED"
}
target: "control-center"
}
IpcHandler {
function open(tab: string): string {
dankDashPopoutLoader.active = true
if (dankDashPopoutLoader.item) {
switch (tab.toLowerCase()) {
case "media":
dankDashPopoutLoader.item.currentTabIndex = 1
break
case "weather":
dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
break
default:
dankDashPopoutLoader.item.currentTabIndex = 0
break
}
dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
dankDashPopoutLoader.item.dashVisible = true
return "DASH_OPEN_SUCCESS"
}
return "DASH_OPEN_FAILED"
}
function close(): string {
if (dankDashPopoutLoader.item) {
dankDashPopoutLoader.item.dashVisible = false
return "DASH_CLOSE_SUCCESS"
}
return "DASH_CLOSE_FAILED"
}
function toggle(tab: string): string {
dankDashPopoutLoader.active = true
if (dankDashPopoutLoader.item) {
if (dankDashPopoutLoader.item.dashVisible) {
dankDashPopoutLoader.item.dashVisible = false
} else {
switch (tab.toLowerCase()) {
case "media":
dankDashPopoutLoader.item.currentTabIndex = 1
break
case "weather":
dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 2 : 0
break
default:
dankDashPopoutLoader.item.currentTabIndex = 0
break
}
dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen)
dankDashPopoutLoader.item.dashVisible = true
}
return "DASH_TOGGLE_SUCCESS"
}
return "DASH_TOGGLE_FAILED"
}
target: "dash"
}
IpcHandler {
function getFocusedScreenName() {
if (CompositorService.isHyprland && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor) {
return Hyprland.focusedWorkspace.monitor.name
}
if (CompositorService.isNiri && NiriService.currentOutput) {
return NiriService.currentOutput
}
return ""
}
function getActiveNotepadInstance() {
if (notepadSlideoutVariants.instances.length === 0) {
return null
}
if (notepadSlideoutVariants.instances.length === 1) {
return notepadSlideoutVariants.instances[0]
}
var focusedScreen = getFocusedScreenName()
if (focusedScreen && notepadSlideoutVariants.instances.length > 0) {
for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) {
var slideout = notepadSlideoutVariants.instances[i]
if (slideout.modelData && slideout.modelData.name === focusedScreen) {
return slideout
}
}
}
for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) {
var slideout = notepadSlideoutVariants.instances[i]
if (slideout.isVisible) {
return slideout
}
}
return notepadSlideoutVariants.instances[0]
}
function open(): string {
var instance = getActiveNotepadInstance()
if (instance) {
instance.show()
return "NOTEPAD_OPEN_SUCCESS"
}
return "NOTEPAD_OPEN_FAILED"
}
function close(): string {
var instance = getActiveNotepadInstance()
if (instance) {
instance.hide()
return "NOTEPAD_CLOSE_SUCCESS"
}
return "NOTEPAD_CLOSE_FAILED"
}
function toggle(): string {
var instance = getActiveNotepadInstance()
if (instance) {
instance.toggle()
return "NOTEPAD_TOGGLE_SUCCESS"
}
return "NOTEPAD_TOGGLE_FAILED"
}
target: "notepad"
}
Variants {
model: SettingsData.getFilteredScreens("toast")
delegate: Toast {
modelData: item
visible: ToastService.toastVisible
}
}
Variants {
model: SettingsData.getFilteredScreens("osd")
delegate: VolumeOSD {
modelData: item
}
}
Variants {
model: SettingsData.getFilteredScreens("osd")
delegate: MicMuteOSD {
modelData: item
}
}
Variants {
model: SettingsData.getFilteredScreens("osd")
delegate: BrightnessOSD {
modelData: item
}
}
Variants {
model: SettingsData.getFilteredScreens("osd")
delegate: IdleInhibitorOSD {
modelData: item
}
}
}

View File

@@ -77,12 +77,15 @@ Item {
width: parent.width width: parent.width
height: parent.height - ClipboardConstants.headerHeight - 70 height: parent.height - ClipboardConstants.headerHeight - 70
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: Theme.surfaceLight
border.color: Theme.outlineLight
border.width: 1
clip: true clip: true
DankListView { DankListView {
id: clipboardListView id: clipboardListView
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingS
model: filteredModel model: filteredModel
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0 currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0

View File

@@ -24,10 +24,17 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (isSelected) { if (isSelected) {
return Theme.primaryPressed return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2)
} }
return mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh return mouseArea.containsMouse ? Theme.primaryHover : Theme.primaryBackground
} }
border.color: {
if (isSelected) {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5)
}
return Theme.outlineStrong
}
border.width: isSelected ? 1.5 : 1
Row { Row {
anchors.fill: parent anchors.fill: parent

View File

@@ -53,7 +53,7 @@ QtObject {
modal.hide() modal.hide()
event.accepted = true event.accepted = true
} }
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) { } else if (event.key === Qt.Key_Down) {
if (!modal.keyboardNavigationActive) { if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true modal.keyboardNavigationActive = true
modal.selectedIndex = 0 modal.selectedIndex = 0
@@ -62,7 +62,7 @@ QtObject {
selectNext() selectNext()
event.accepted = true event.accepted = true
} }
} else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) { } else if (event.key === Qt.Key_Up) {
if (!modal.keyboardNavigationActive) { if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true modal.keyboardNavigationActive = true
modal.selectedIndex = 0 modal.selectedIndex = 0
@@ -74,42 +74,6 @@ QtObject {
selectPrevious() selectPrevious()
event.accepted = true event.accepted = true
} }
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true
modal.selectedIndex = 0
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true
modal.selectedIndex = 0
} else if (modal.selectedIndex === 0) {
modal.keyboardNavigationActive = false
} else {
selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true
modal.selectedIndex = 0
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true
modal.selectedIndex = 0
} else if (modal.selectedIndex === 0) {
modal.keyboardNavigationActive = false
} else {
selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) { } else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) {
modal.clearAll() modal.clearAll()
modal.hide() modal.hide()

View File

@@ -93,48 +93,6 @@ DankModal {
selectedButton = 1 selectedButton = 1
event.accepted = true event.accepted = true
break break
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = (selectedButton + 1) % 2
event.accepted = true
}
break
case Qt.Key_P:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2
event.accepted = true
}
break
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = 1
event.accepted = true
}
break
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = 0
event.accepted = true
}
break
case Qt.Key_H:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = 0
event.accepted = true
}
break
case Qt.Key_L:
if (event.modifiers & Qt.ControlModifier) {
keyboardNavigation = true
selectedButton = 1
event.accepted = true
}
break
case Qt.Key_Tab: case Qt.Key_Tab:
keyboardNavigation = true keyboardNavigation = true
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2 selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2

View File

@@ -22,7 +22,7 @@ PanelWindow {
property bool closeOnEscapeKey: true property bool closeOnEscapeKey: true
property bool closeOnBackgroundClick: true property bool closeOnBackgroundClick: true
property string animationType: "scale" property string animationType: "scale"
property int animationDuration: Theme.shortDuration property int animationDuration: Theme.shorterDuration
property var animationEasing: Theme.emphasizedEasing property var animationEasing: Theme.emphasizedEasing
property color backgroundColor: Theme.surfaceContainer property color backgroundColor: Theme.surfaceContainer
property color borderColor: Theme.outlineMedium property color borderColor: Theme.outlineMedium
@@ -34,7 +34,6 @@ PanelWindow {
property bool shouldHaveFocus: shouldBeVisible property bool shouldHaveFocus: shouldBeVisible
property bool allowFocusOverride: false property bool allowFocusOverride: false
property bool allowStacking: false property bool allowStacking: false
property bool keepContentLoaded: false
signal opened signal opened
signal dialogClosed signal dialogClosed
@@ -91,7 +90,7 @@ PanelWindow {
Timer { Timer {
id: closeTimer id: closeTimer
interval: animationDuration + 100 interval: animationDuration + 50
onTriggered: { onTriggered: {
visible = false visible = false
} }
@@ -157,7 +156,9 @@ PanelWindow {
radius: root.cornerRadius radius: root.cornerRadius
border.color: root.borderColor border.color: root.borderColor
border.width: root.borderWidth border.width: root.borderWidth
layer.enabled: true layer.enabled: root.enableShadow
opacity: root.shouldBeVisible ? 1 : 0
scale: root.animationType === "scale" ? (root.shouldBeVisible ? 1 : 0.9) : 1
transform: root.animationType === "slide" ? slideTransform : null transform: root.animationType === "slide" ? slideTransform : null
Translate { Translate {
@@ -171,10 +172,26 @@ PanelWindow {
id: contentLoader id: contentLoader
anchors.fill: parent anchors.fill: parent
active: root.keepContentLoaded || root.shouldBeVisible || root.visible active: root.visible
asynchronous: false asynchronous: false
} }
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: root.animationEasing
}
}
Behavior on scale {
enabled: root.animationType === "scale"
NumberAnimation {
duration: root.animationDuration
easing.type: root.animationEasing
}
}
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
@@ -182,15 +199,6 @@ PanelWindow {
shadowBlur: 1 shadowBlur: 1
shadowColor: Theme.shadowStrong shadowColor: Theme.shadowStrong
shadowOpacity: 0.3 shadowOpacity: 0.3
source: contentContainer
opacity: root.shouldBeVisible ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: animationDuration
easing.type: animationEasing
}
}
} }
} }
@@ -199,8 +207,8 @@ PanelWindow {
objectName: "modalFocusScope" objectName: "modalFocusScope"
anchors.fill: parent anchors.fill: parent
visible: root.shouldBeVisible || root.visible visible: root.visible // Only active when the modal is visible
focus: root.shouldBeVisible focus: root.visible
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
if (root.closeOnEscapeKey && shouldHaveFocus) { if (root.closeOnEscapeKey && shouldHaveFocus) {
root.close() root.close()
@@ -215,7 +223,7 @@ PanelWindow {
Connections { Connections {
function onShouldHaveFocusChanged() { function onShouldHaveFocusChanged() {
if (shouldHaveFocus && shouldBeVisible) { if (shouldHaveFocus && visible) {
Qt.callLater(() => focusScope.forceActiveFocus()) Qt.callLater(() => focusScope.forceActiveFocus())
} }
} }

View File

@@ -239,12 +239,7 @@ DankModal {
return return
} }
if (!keyboardNavigationActive) { if (!keyboardNavigationActive) {
const isInitKey = event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right || if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right) {
(event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) ||
(event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) ||
(event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier)
if (isInitKey) {
keyboardNavigationActive = true keyboardNavigationActive = true
if (currentPath !== homeDir) { if (currentPath !== homeDir) {
backButtonFocused = true backButtonFocused = true
@@ -286,69 +281,6 @@ DankModal {
} }
event.accepted = true event.accepted = true
break break
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else if (selectedIndex < totalItems - 1) {
selectedIndex++
}
event.accepted = true
}
break
case Qt.Key_P:
if (event.modifiers & Qt.ControlModifier) {
if (selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
}
break
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
if (selectedIndex < totalItems - 1) {
selectedIndex++
}
event.accepted = true
}
break
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
if (selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
}
break
case Qt.Key_H:
if (event.modifiers & Qt.ControlModifier) {
if (!backButtonFocused && selectedIndex > 0) {
selectedIndex--
} else if (currentPath !== homeDir) {
backButtonFocused = true
selectedIndex = -1
}
event.accepted = true
}
break
case Qt.Key_L:
if (event.modifiers & Qt.ControlModifier) {
if (backButtonFocused) {
backButtonFocused = false
selectedIndex = 0
} else if (selectedIndex < totalItems - 1) {
selectedIndex++
}
event.accepted = true
}
break
case Qt.Key_Left: case Qt.Key_Left:
if (backButtonFocused) if (backButtonFocused)
return return

View File

@@ -9,25 +9,9 @@ DankModal {
property int selectedIndex: 0 property int selectedIndex: 0
property int optionCount: SessionService.hibernateSupported ? 5 : 4 property int optionCount: SessionService.hibernateSupported ? 5 : 4
property rect parentBounds: Qt.rect(0, 0, 0, 0)
property var parentScreen: null
signal powerActionRequested(string action, string title, string message) signal powerActionRequested(string action, string title, string message)
function openCentered() {
parentBounds = Qt.rect(0, 0, 0, 0)
parentScreen = null
backgroundOpacity = 0.5
open()
}
function openFromControlCenter(bounds, targetScreen) {
parentBounds = bounds
parentScreen = targetScreen
backgroundOpacity = 0
open()
}
function selectOption(action) { function selectOption(action) {
close(); close();
const actions = { const actions = {
@@ -63,16 +47,6 @@ DankModal {
width: 320 width: 320
height: contentLoader.item ? contentLoader.item.implicitHeight : 300 height: contentLoader.item ? contentLoader.item.implicitHeight : 300
enableShadow: true enableShadow: true
screen: parentScreen
positioning: parentBounds.width > 0 ? "custom" : "center"
customPosition: {
if (parentBounds.width > 0) {
const centerX = parentBounds.x + (parentBounds.width - width) / 2
const centerY = parentBounds.y + (parentBounds.height - height) / 2
return Qt.point(centerX, centerY)
}
return Qt.point(0, 0)
}
onBackgroundClicked: () => { onBackgroundClicked: () => {
return close(); return close();
} }
@@ -83,11 +57,13 @@ DankModal {
modalFocusScope.Keys.onPressed: (event) => { modalFocusScope.Keys.onPressed: (event) => {
switch (event.key) { switch (event.key) {
case Qt.Key_Up: case Qt.Key_Up:
case Qt.Key_Backtab:
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount; selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
event.accepted = true; event.accepted = true;
break; break;
case Qt.Key_Down: case Qt.Key_Down:
selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true;
break;
case Qt.Key_Tab: case Qt.Key_Tab:
selectedIndex = (selectedIndex + 1) % optionCount; selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true; event.accepted = true;
@@ -102,30 +78,6 @@ DankModal {
} }
event.accepted = true; event.accepted = true;
break; break;
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true;
}
break;
case Qt.Key_P:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
event.accepted = true;
}
break;
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true;
}
break;
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
event.accepted = true;
}
break;
} }
} }

View File

@@ -79,6 +79,7 @@ Item {
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"] property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
width: parent.width
text: "Automatically lock after" text: "Automatically lock after"
options: timeoutOptions options: timeoutOptions
@@ -115,6 +116,7 @@ Item {
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"] property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
width: parent.width
text: "Turn off monitors after" text: "Turn off monitors after"
options: timeoutOptions options: timeoutOptions
@@ -151,6 +153,7 @@ Item {
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"] property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
width: parent.width
text: "Suspend system after" text: "Suspend system after"
options: timeoutOptions options: timeoutOptions
@@ -187,6 +190,7 @@ Item {
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"] property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
width: parent.width
text: "Hibernate system after" text: "Hibernate system after"
options: timeoutOptions options: timeoutOptions
visible: SessionService.hibernateSupported visible: SessionService.hibernateSupported

View File

@@ -67,7 +67,7 @@ Item {
visible: active visible: active
asynchronous: true asynchronous: true
sourceComponent: DankBarTab { sourceComponent: TopBarTab {
} }
} }
@@ -153,24 +153,11 @@ Item {
} }
Loader {
id: pluginsLoader
anchors.fill: parent
active: root.currentIndex === 10
visible: active
asynchronous: true
sourceComponent: PluginsTab {
}
}
Loader { Loader {
id: aboutLoader id: aboutLoader
anchors.fill: parent anchors.fill: parent
active: root.currentIndex === 11 active: root.currentIndex === 10
visible: active visible: active
asynchronous: true asynchronous: true

View File

@@ -34,7 +34,7 @@ DankModal {
objectName: "settingsModal" objectName: "settingsModal"
width: 800 width: 800
height: 800 height: 750
visible: false visible: false
onBackgroundClicked: () => { onBackgroundClicked: () => {
return hide(); return hide();

View File

@@ -18,7 +18,7 @@ Rectangle {
"text": "Weather", "text": "Weather",
"icon": "cloud" "icon": "cloud"
}, { }, {
"text": "Dank Bar", "text": "Top Bar",
"icon": "toolbar" "icon": "toolbar"
}, { }, {
"text": "Widgets", "text": "Widgets",
@@ -38,9 +38,6 @@ Rectangle {
}, { }, {
"text": "Power", "text": "Power",
"icon": "power_settings_new" "icon": "power_settings_new"
}, {
"text": "Plugins",
"icon": "extension"
}, { }, {
"text": "About", "text": "About",
"icon": "info" "icon": "info"
@@ -86,7 +83,7 @@ Rectangle {
width: parent.width - Theme.spacingS * 2 width: parent.width - Theme.spacingS * 2
height: 44 height: 44
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: isActive ? Theme.primary : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent" color: isActive ? Theme.primaryContainer : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -97,14 +94,14 @@ Rectangle {
DankIcon { DankIcon {
name: modelData.icon || "" name: modelData.icon || ""
size: Theme.iconSize - 2 size: Theme.iconSize - 2
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
StyledText { StyledText {
text: modelData.text || "" text: modelData.text || ""
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: parent.parent.isActive ? Theme.primaryText : Theme.surfaceText color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }

View File

@@ -33,46 +33,6 @@ Item {
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") { } else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
appLauncher.selectPreviousInRow() appLauncher.selectPreviousInRow()
event.accepted = true event.accepted = true
} else if (event.key == Qt.Key_J && event.modifiers & Qt.ControlModifier) {
appLauncher.selectNext()
event.accepted = true
} else if (event.key == Qt.Key_K && event.modifiers & Qt.ControlModifier) {
appLauncher.selectPrevious()
event.accepted = true
} else if (event.key == Qt.Key_L && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
appLauncher.selectNextInRow()
event.accepted = true
} else if (event.key == Qt.Key_H && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
appLauncher.selectPreviousInRow()
event.accepted = true
} else if (event.key === Qt.Key_Tab) {
if (appLauncher.viewMode === "grid") {
appLauncher.selectNextInRow()
} else {
appLauncher.selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_Backtab) {
if (appLauncher.viewMode === "grid") {
appLauncher.selectPreviousInRow()
} else {
appLauncher.selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
if (appLauncher.viewMode === "grid") {
appLauncher.selectNextInRow()
} else {
appLauncher.selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
if (appLauncher.viewMode === "grid") {
appLauncher.selectPreviousInRow()
} else {
appLauncher.selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
appLauncher.launchSelected() appLauncher.launchSelected()
event.accepted = true event.accepted = true
@@ -99,21 +59,23 @@ Item {
Column { Column {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingL
spacing: Theme.spacingM spacing: Theme.spacingL
Rectangle { Rectangle {
width: parent.width width: parent.width
height: categorySelector.height + Theme.spacingS * 2 height: categorySelector.height + Theme.spacingM * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: Theme.surfaceVariantAlpha
border.color: Theme.outlineMedium
border.width: 1
visible: appLauncher.categories.length > 1 || appLauncher.model.count > 0 visible: appLauncher.categories.length > 1 || appLauncher.model.count > 0
CategorySelector { CategorySelector {
id: categorySelector id: categorySelector
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - Theme.spacingS * 2 width: parent.width - Theme.spacingM * 2
categories: appLauncher.categories categories: appLauncher.categories
selectedCategory: appLauncher.selectedCategory selectedCategory: appLauncher.selectedCategory
compact: false compact: false
@@ -126,12 +88,11 @@ Item {
Row { Row {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
leftPadding: Theme.spacingS
DankTextField { DankTextField {
id: searchField id: searchField
width: parent.width - 80 - Theme.spacingL width: parent.width - 80 - Theme.spacingM
height: 56 height: 56
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
backgroundColor: Theme.surfaceContainerHigh backgroundColor: Theme.surfaceContainerHigh
@@ -164,7 +125,7 @@ Item {
else if (appLauncher.model.count > 0) else if (appLauncher.model.count > 0)
appLauncher.launchApp(appLauncher.model.get(0)) appLauncher.launchApp(appLauncher.model.get(0))
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) { } else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
event.accepted = false event.accepted = false
} }
} }
@@ -180,6 +141,8 @@ Item {
height: 36 height: 36
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent" color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
border.color: appLauncher.viewMode === "list" ? Theme.primarySelected : "transparent"
border.width: 1
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -205,6 +168,8 @@ Item {
height: 36 height: 36
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent" color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
border.color: appLauncher.viewMode === "grid" ? Theme.primarySelected : "transparent"
border.width: 1
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent

View File

@@ -32,7 +32,11 @@ DankModal {
function hide() { function hide() {
spotlightOpen = false spotlightOpen = false
close() close()
cleanupTimer.restart() if (contentLoader.item && contentLoader.item.appLauncher) {
contentLoader.item.appLauncher.searchQuery = ""
contentLoader.item.appLauncher.selectedIndex = 0
contentLoader.item.appLauncher.setCategory("All")
}
} }
function toggle() { function toggle() {
@@ -51,7 +55,6 @@ DankModal {
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
borderWidth: 1 borderWidth: 1
enableShadow: true enableShadow: true
keepContentLoaded: true
onVisibleChanged: () => { onVisibleChanged: () => {
if (visible && !spotlightOpen) { if (visible && !spotlightOpen) {
show() show()
@@ -69,19 +72,6 @@ DankModal {
} }
content: spotlightContent content: spotlightContent
Timer {
id: cleanupTimer
interval: animationDuration + 50
onTriggered: {
if (contentLoader.item && contentLoader.item.appLauncher) {
contentLoader.item.appLauncher.searchQuery = ""
contentLoader.item.appLauncher.selectedIndex = 0
contentLoader.item.appLauncher.setCategory("All")
}
}
}
Connections { Connections {
function onCloseAllModalsExcept(excludedModal) { function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) { if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {

View File

@@ -13,7 +13,9 @@ Rectangle {
width: parent.width width: parent.width
height: parent.height - y height: parent.height - y
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: Theme.surfaceContainerHigh
border.color: Theme.outlineLight
border.width: 1
DankListView { DankListView {
id: resultsList id: resultsList
@@ -73,7 +75,9 @@ Rectangle {
width: ListView.view.width width: ListView.view.width
height: resultsList.itemHeight height: resultsList.itemHeight
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent"
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
border.width: ListView.isCurrentItem ? 2 : 1
Row { Row {
anchors.fill: parent anchors.fill: parent
@@ -234,7 +238,9 @@ Rectangle {
width: resultsGrid.cellWidth - resultsGrid.cellPadding width: resultsGrid.cellWidth - resultsGrid.cellPadding
height: resultsGrid.cellHeight - resultsGrid.cellPadding height: resultsGrid.cellHeight - resultsGrid.cellPadding
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent"
border.color: resultsGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
border.width: resultsGrid.currentIndex === index ? 2 : 1
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent

View File

@@ -12,6 +12,7 @@ import qs.Widgets
DankPopout { DankPopout {
id: appDrawerPopout id: appDrawerPopout
property string triggerSection: "left"
property var triggerScreen: null property var triggerScreen: null
// Setting to Exclusive, so virtual keyboards can send input to app drawer // Setting to Exclusive, so virtual keyboards can send input to app drawer
@@ -32,9 +33,9 @@ DankPopout {
popupWidth: 520 popupWidth: 520
popupHeight: 600 popupHeight: 600
triggerX: Theme.spacingL triggerX: Theme.spacingL
triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2 triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 40 triggerWidth: 40
positioning: "" positioning: "center"
screen: triggerScreen screen: triggerScreen
onShouldBeVisibleChanged: { onShouldBeVisibleChanged: {
@@ -94,7 +95,7 @@ DankPopout {
color: "transparent" color: "transparent"
radius: parent.radius + Math.abs(modelData.margin) radius: parent.radius + Math.abs(modelData.margin)
border.color: modelData.color border.color: modelData.color
border.width: 0 border.width: 1
z: modelData.z z: modelData.z
} }
} }
@@ -127,44 +128,6 @@ DankPopout {
return return
} }
if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
appLauncher.selectNext()
event.accepted = true
return
}
if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
appLauncher.selectPrevious()
event.accepted = true
return
}
if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
appLauncher.selectNext()
event.accepted = true
return
}
if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
appLauncher.selectPrevious()
event.accepted = true
return
}
if (appLauncher.viewMode === "grid") {
if (event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier) {
appLauncher.selectNextInRow()
event.accepted = true
return
}
if (event.key === Qt.Key_H && event.modifiers & Qt.ControlModifier) {
appLauncher.selectPreviousInRow()
event.accepted = true
return
}
}
if (!searchField.activeFocus && event.text && /[a-zA-Z0-9\s]/.test(event.text)) { if (!searchField.activeFocus && event.text && /[a-zA-Z0-9\s]/.test(event.text)) {
searchField.forceActiveFocus() searchField.forceActiveFocus()
searchField.insertText(event.text) searchField.insertText(event.text)
@@ -173,16 +136,15 @@ DankPopout {
} }
Column { Column {
width: parent.width - Theme.spacingS * 2 width: parent.width - Theme.spacingL * 2
height: parent.height - Theme.spacingS * 2 height: parent.height - Theme.spacingL * 2
x: Theme.spacingS x: Theme.spacingL
y: Theme.spacingS y: Theme.spacingL
spacing: Theme.spacingS spacing: Theme.spacingL
Row { Row {
width: parent.width width: parent.width
height: 40 height: 40
leftPadding: Theme.spacingS
StyledText { StyledText {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -208,8 +170,7 @@ DankPopout {
DankTextField { DankTextField {
id: searchField id: searchField
width: parent.width - Theme.spacingS * 2 width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
height: 52 height: 52
cornerRadius: Theme.cornerRadius cornerRadius: Theme.cornerRadius
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
@@ -270,18 +231,14 @@ DankPopout {
height: 40 height: 40
spacing: Theme.spacingM spacing: Theme.spacingM
visible: searchField.text.length === 0 visible: searchField.text.length === 0
leftPadding: Theme.spacingS
Rectangle { Item {
width: 180 width: 200
height: 40 height: 36
radius: Theme.cornerRadius
color: "transparent"
DankDropdown { DankDropdown {
anchors.fill: parent anchors.fill: parent
text: "" text: ""
dropdownWidth: 180
currentValue: appLauncher.selectedCategory currentValue: appLauncher.selectedCategory
options: appLauncher.categories options: appLauncher.categories
optionIcons: appLauncher.categoryIcons optionIcons: appLauncher.categoryIcons
@@ -292,7 +249,7 @@ DankPopout {
} }
Item { Item {
width: parent.width - 290 width: parent.width - 300
height: 1 height: 1
} }
@@ -329,13 +286,15 @@ DankPopout {
Rectangle { Rectangle {
width: parent.width width: parent.width
height: { height: {
let usedHeight = 40 + Theme.spacingS let usedHeight = 40 + Theme.spacingL
usedHeight += 52 + Theme.spacingS usedHeight += 52 + Theme.spacingL
usedHeight += (searchField.text.length === 0 ? 40 : 0) usedHeight += (searchField.text.length === 0 ? 40 + Theme.spacingL : 0)
return parent.height - usedHeight return parent.height - usedHeight
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "transparent" color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
border.width: 1
DankListView { DankListView {
id: appList id: appList
@@ -364,9 +323,7 @@ DankPopout {
} }
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS anchors.margins: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: appLauncher.viewMode === "list" visible: appLauncher.viewMode === "list"
model: appLauncher.model model: appLauncher.model
currentIndex: appLauncher.selectedIndex currentIndex: appLauncher.selectedIndex
@@ -396,7 +353,9 @@ DankPopout {
width: ListView.view.width width: ListView.view.width
height: appList.itemHeight height: appList.itemHeight
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
border.width: ListView.isCurrentItem ? 2 : 1
Row { Row {
anchors.fill: parent anchors.fill: parent
@@ -412,7 +371,6 @@ DankPopout {
id: listIconImg id: listIconImg
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingXS
source: Quickshell.iconPath(model.icon, true) source: Quickshell.iconPath(model.icon, true)
smooth: true smooth: true
asynchronous: true asynchronous: true
@@ -421,13 +379,10 @@ DankPopout {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingM
visible: !listIconImg.visible visible: !listIconImg.visible
color: Theme.surfaceLight color: Theme.surfaceLight
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 0 border.width: 1
border.color: Theme.primarySelected border.color: Theme.primarySelected
StyledText { StyledText {
@@ -470,9 +425,6 @@ DankPopout {
id: listMouseArea id: listMouseArea
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingM
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
@@ -532,9 +484,7 @@ DankPopout {
} }
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS anchors.margins: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: appLauncher.viewMode === "grid" visible: appLauncher.viewMode === "grid"
model: appLauncher.model model: appLauncher.model
clip: true clip: true
@@ -566,7 +516,9 @@ DankPopout {
width: appGrid.cellWidth - appGrid.cellPadding width: appGrid.cellWidth - appGrid.cellPadding
height: appGrid.cellHeight - appGrid.cellPadding height: appGrid.cellHeight - appGrid.cellPadding
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
border.color: appGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
border.width: appGrid.currentIndex === index ? 2 : 1
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
@@ -583,9 +535,6 @@ DankPopout {
id: gridIconImg id: gridIconImg
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
source: Quickshell.iconPath(model.icon, true) source: Quickshell.iconPath(model.icon, true)
smooth: true smooth: true
asynchronous: true asynchronous: true
@@ -594,13 +543,10 @@ DankPopout {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: !gridIconImg.visible visible: !gridIconImg.visible
color: Theme.surfaceLight color: Theme.surfaceLight
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 0 border.width: 1
border.color: Theme.primarySelected border.color: Theme.primarySelected
StyledText { StyledText {
@@ -631,9 +577,6 @@ DankPopout {
id: gridMouseArea id: gridMouseArea
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
@@ -710,7 +653,7 @@ DankPopout {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.popupBackground() color: Theme.popupBackground()
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 border.width: 1
z: 1000 z: 1000
opacity: menuVisible ? 1 : 0 opacity: menuVisible ? 1 : 0
scale: menuVisible ? 1 : 0.85 scale: menuVisible ? 1 : 0.85

View File

@@ -15,7 +15,7 @@ Item {
readonly property int maxCompactItems: 8 readonly property int maxCompactItems: 8
readonly property int itemHeight: 36 readonly property int itemHeight: 36
readonly property color selectedBorderColor: "transparent" readonly property color selectedBorderColor: "transparent"
readonly property color unselectedBorderColor: "transparent" readonly property color unselectedBorderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
function handleCategoryClick(category) { function handleCategoryClick(category) {
selectedCategory = category selectedCategory = category
@@ -42,7 +42,8 @@ Item {
height: root.itemHeight height: root.itemHeight
width: root.getButtonWidth(itemCount, parent.width) width: root.getButtonWidth(itemCount, parent.width)
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
@@ -81,7 +82,7 @@ Item {
height: root.itemHeight height: root.itemHeight
width: root.getButtonWidth(itemCount, parent.width) width: root.getButtonWidth(itemCount, parent.width)
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
StyledText { StyledText {
@@ -117,7 +118,7 @@ Item {
height: root.itemHeight height: root.itemHeight
width: root.getButtonWidth(itemCount, parent.width) width: root.getButtonWidth(itemCount, parent.width)
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh color: selectedCategory === modelData ? Theme.primary : "transparent"
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
StyledText { StyledText {

View File

@@ -59,7 +59,7 @@ Rectangle {
DankIcon { DankIcon {
name: root.iconName name: root.iconName
size: Theme.iconSize size: Theme.iconSize
color: isActive ? Theme.primaryText : Theme.primary color: isActive ? Theme.primaryContainer : Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -77,7 +77,7 @@ Rectangle {
width: parent.width width: parent.width
text: root.text text: root.text
style: Typography.Style.Body style: Typography.Style.Body
color: isActive ? Theme.primaryText : Theme.surfaceText color: isActive ? Theme.primaryContainer : Theme.surfaceText
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
} }
@@ -86,7 +86,7 @@ Rectangle {
width: parent.width width: parent.width
text: root.secondaryText text: root.secondaryText
style: Typography.Style.Caption style: Typography.Style.Caption
color: isActive ? Theme.primaryText : Theme.surfaceVariantText color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
visible: text.length > 0 visible: text.length > 0
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap

View File

@@ -7,7 +7,6 @@ Item {
property string expandedSection: "" property string expandedSection: ""
property var expandedWidgetData: null property var expandedWidgetData: null
property var bluetoothCodecSelector: null
Loader { Loader {
width: parent.width width: parent.width
@@ -43,17 +42,7 @@ Item {
Component { Component {
id: bluetoothDetailComponent id: bluetoothDetailComponent
BluetoothDetail { BluetoothDetail {}
id: bluetoothDetail
onShowCodecSelector: function(device) {
if (root.bluetoothCodecSelector) {
root.bluetoothCodecSelector.show(device)
root.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) {
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
})
}
}
}
} }
Component { Component {

View File

@@ -1,91 +0,0 @@
import QtQuick
import qs.Common
import qs.Modules.ControlCenter.Details
Item {
id: root
property string expandedSection: ""
property var expandedWidgetData: null
height: active ? 250 : 0
visible: active
readonly property bool active: expandedSection !== ""
Behavior on height {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutCubic
}
}
Loader {
anchors.fill: parent
anchors.topMargin: Theme.spacingS
sourceComponent: {
if (!root.active) return null
if (expandedSection.startsWith("diskUsage_")) {
return diskUsageDetailComponent
}
switch (expandedSection) {
case "wifi": return networkDetailComponent
case "bluetooth": return bluetoothDetailComponent
case "audioOutput": return audioOutputDetailComponent
case "audioInput": return audioInputDetailComponent
case "battery": return batteryDetailComponent
default: return null
}
}
}
Component {
id: networkDetailComponent
NetworkDetail {}
}
Component {
id: bluetoothDetailComponent
BluetoothDetail {}
}
Component {
id: audioOutputDetailComponent
AudioOutputDetail {}
}
Component {
id: audioInputDetailComponent
AudioInputDetail {}
}
Component {
id: batteryDetailComponent
BatteryDetail {}
}
Component {
id: diskUsageDetailComponent
DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || ""
onMountPathChanged: (newMountPath) => {
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.mountPath = newMountPath
return updatedWidget
}
return w
})
SettingsData.setControlCenterWidgets(newWidgets)
}
}
}
}
}

View File

@@ -1,289 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
property bool editMode: false
property var widgetData: null
property int widgetIndex: -1
property bool isSlider: false
property Component widgetComponent: null
property real gridCellWidth: 100
property real gridCellHeight: 60
property int gridColumns: 4
property var gridLayout: null
z: dragArea.drag.active ? 10000 : 1
signal widgetMoved(int fromIndex, int toIndex)
signal removeWidget(int index)
signal toggleWidgetSize(int index)
width: {
const widgetWidth = widgetData?.width || 50
if (widgetWidth <= 25) return gridCellWidth
else if (widgetWidth <= 50) return gridCellWidth * 2
else if (widgetWidth <= 75) return gridCellWidth * 3
else return gridCellWidth * 4
}
height: isSlider ? 16 : gridCellHeight
Rectangle {
id: dragIndicator
anchors.fill: parent
color: "transparent"
border.color: Theme.primary
border.width: dragArea.drag.active ? 2 : 0
radius: Theme.cornerRadius
opacity: dragArea.drag.active ? 0.8 : 1.0
z: dragArea.drag.active ? 10000 : 1
Behavior on border.width {
NumberAnimation { duration: 150 }
}
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
Loader {
id: widgetLoader
anchors.fill: parent
sourceComponent: widgetComponent
property var widgetData: root.widgetData
property int widgetIndex: root.widgetIndex
property int globalWidgetIndex: root.widgetIndex
property int widgetWidth: root.widgetData?.width || 50
MouseArea {
id: editModeBlocker
anchors.fill: parent
enabled: root.editMode
acceptedButtons: Qt.AllButtons
onPressed: function(mouse) { mouse.accepted = true }
onWheel: function(wheel) { wheel.accepted = true }
z: 100
}
}
MouseArea {
id: dragArea
anchors.fill: parent
enabled: editMode
cursorShape: editMode ? Qt.OpenHandCursor : Qt.PointingHandCursor
drag.target: editMode ? root : null
drag.axis: Drag.XAndYAxis
drag.smoothed: true
onPressed: function(mouse) {
if (editMode) {
cursorShape = Qt.ClosedHandCursor
if (root.gridLayout && root.gridLayout.moveToTop) {
root.gridLayout.moveToTop(root)
}
}
}
onReleased: function(mouse) {
if (editMode) {
cursorShape = Qt.OpenHandCursor
root.snapToGrid()
}
}
}
Drag.active: dragArea.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
function swapIndices(i, j) {
if (i === j) return;
const arr = SettingsData.controlCenterWidgets;
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
const copy = arr.slice();
const tmp = copy[i];
copy[i] = copy[j];
copy[j] = tmp;
SettingsData.setControlCenterWidgets(copy);
}
function snapToGrid() {
if (!editMode || !gridLayout) return
const globalPos = root.mapToItem(gridLayout, 0, 0)
const cellWidth = gridLayout.width / gridColumns
const cellHeight = gridCellHeight + Theme.spacingS
const centerX = globalPos.x + (root.width / 2)
const centerY = globalPos.y + (root.height / 2)
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
targetCol = Math.min(targetCol, gridColumns - 1)
const newIndex = findBestInsertionIndex(targetRow, targetCol)
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
swapIndices(widgetIndex, newIndex)
}
}
function findBestInsertionIndex(targetRow, targetCol) {
const widgets = SettingsData.controlCenterWidgets || [];
const n = widgets.length;
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
function spanFor(width) {
const w = width ?? 50;
if (w <= 25) return 1;
if (w <= 50) return 2;
if (w <= 75) return 3;
return 4;
}
const cols = gridColumns || 4;
let row = 0, col = 0;
let draggedOrigKey = null;
const pos = [];
for (let i = 0; i < n; i++) {
const span = Math.min(spanFor(widgets[i].width), cols);
if (col + span > cols) {
row++;
col = 0;
}
const startCol = col;
const centerKey = row * cols + (startCol + (span - 1) / 2);
if (i === widgetIndex) {
draggedOrigKey = centerKey;
} else {
pos.push({ index: i, row, startCol, span, centerKey });
}
col += span;
if (col >= cols) {
row++;
col = 0;
}
}
if (pos.length === 0) return -1;
const centerColCoord = targetCol + 0.5;
const targetKey = targetRow * cols + centerColCoord;
for (let k = 0; k < pos.length; k++) {
const p = pos[k];
if (p.row === targetRow && centerColCoord >= p.startCol && centerColCoord < (p.startCol + p.span)) {
return p.index;
}
}
let lo = 0, hi = pos.length - 1;
if (targetKey <= pos[0].centerKey) return pos[0].index;
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
while (lo <= hi) {
const mid = (lo + hi) >> 1;
const mk = pos[mid].centerKey;
if (targetKey < mk) hi = mid - 1;
else if (targetKey > mk) lo = mid + 1;
else return pos[mid].index;
}
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
return (movingUp ? pos[lo].index : pos[hi].index);
}
Rectangle {
width: 16
height: 16
radius: 8
color: Theme.error
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: -4
visible: editMode
z: 10
DankIcon {
anchors.centerIn: parent
name: "close"
size: 12
color: Theme.primaryText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: removeWidget(widgetIndex)
}
}
SizeControls {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: -6
visible: editMode
z: 10
currentSize: root.widgetData?.width || 50
isSlider: root.isSlider
widgetIndex: root.widgetIndex
onSizeChanged: (newSize) => {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = newSize
SettingsData.setControlCenterWidgets(widgets)
}
}
}
Rectangle {
id: dragHandle
width: 16
height: 12
radius: 2
color: Theme.primary
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 4
visible: editMode
z: 15
opacity: dragArea.drag.active ? 1.0 : 0.7
DankIcon {
anchors.centerIn: parent
name: "drag_indicator"
size: 10
color: Theme.primaryText
}
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
Rectangle {
anchors.fill: parent
color: editMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.cornerRadius
border.color: "transparent"
border.width: 0
z: -1
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
}
}

View File

@@ -7,7 +7,6 @@ Row {
id: root id: root
property var availableWidgets: [] property var availableWidgets: []
property Item popoutContent: null
signal addWidget(string widgetId) signal addWidget(string widgetId)
signal resetToDefault() signal resetToDefault()
@@ -20,9 +19,7 @@ Row {
Popup { Popup {
id: addWidgetPopup id: addWidgetPopup
parent: popoutContent anchors.centerIn: parent
x: parent ? Math.round((parent.width - width) / 2) : 0
y: parent ? Math.round((parent.height - height) / 2) : 0
width: 400 width: 400
height: 300 height: 300
modal: true modal: true
@@ -32,7 +29,7 @@ Row {
background: Rectangle { background: Rectangle {
color: Theme.surfaceContainer color: Theme.surfaceContainer
border.color: Theme.primarySelected border.color: Theme.primarySelected
border.width: 0 border.width: 1
radius: Theme.cornerRadius radius: Theme.cornerRadius
} }
@@ -77,7 +74,7 @@ Row {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: widgetMouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceContainerHigh color: widgetMouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceContainerHigh
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: 0 border.width: 1
Row { Row {
anchors.fill: parent anchors.fill: parent
@@ -141,7 +138,7 @@ Row {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
border.color: Theme.primary border.color: Theme.primary
border.width: 0 border.width: 1
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
@@ -175,7 +172,7 @@ Row {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
border.color: Theme.warning border.color: Theme.warning
border.width: 0 border.width: 1
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
@@ -209,7 +206,7 @@ Row {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
border.color: Theme.error border.color: Theme.error
border.width: 0 border.width: 1
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent

View File

@@ -0,0 +1,241 @@
import QtQuick
import qs.Common
import qs.Widgets
Item {
id: root
property bool editMode: false
property var widgetData: null
property int widgetIndex: -1
property bool showSizeControls: true
property bool isSlider: false
signal removeWidget(int index)
signal toggleWidgetSize(int index)
signal moveWidget(int fromIndex, int toIndex)
// Delete button in top-right
Rectangle {
width: 16
height: 16
radius: 8
color: Theme.error
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: -4
visible: editMode
z: 10
DankIcon {
anchors.centerIn: parent
name: "close"
size: 12
color: Theme.primaryText
}
MouseArea {
anchors.fill: parent
onClicked: root.removeWidget(widgetIndex)
}
}
// Size control buttons in bottom-right
Row {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: -8
spacing: 4
visible: editMode && showSizeControls
z: 10
Rectangle {
width: 24
height: 24
radius: 12
color: (widgetData?.width || 50) === 25 ? Theme.primary : Theme.primaryContainer
border.color: Theme.primary
border.width: 1
visible: !isSlider
StyledText {
anchors.centerIn: parent
text: "25"
font.pixelSize: 10
font.weight: Font.Medium
color: (widgetData?.width || 50) === 25 ? Theme.primaryText : Theme.primary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = 25
SettingsData.setControlCenterWidgets(widgets)
}
}
}
}
Rectangle {
width: 24
height: 24
radius: 12
color: (widgetData?.width || 50) === 50 ? Theme.primary : Theme.primaryContainer
border.color: Theme.primary
border.width: 1
StyledText {
anchors.centerIn: parent
text: "50"
font.pixelSize: 10
font.weight: Font.Medium
color: (widgetData?.width || 50) === 50 ? Theme.primaryText : Theme.primary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = 50
SettingsData.setControlCenterWidgets(widgets)
}
}
}
}
Rectangle {
width: 24
height: 24
radius: 12
color: (widgetData?.width || 50) === 75 ? Theme.primary : Theme.primaryContainer
border.color: Theme.primary
border.width: 1
visible: !isSlider
StyledText {
anchors.centerIn: parent
text: "75"
font.pixelSize: 10
font.weight: Font.Medium
color: (widgetData?.width || 50) === 75 ? Theme.primaryText : Theme.primary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = 75
SettingsData.setControlCenterWidgets(widgets)
}
}
}
}
Rectangle {
width: 24
height: 24
radius: 12
color: (widgetData?.width || 50) === 100 ? Theme.primary : Theme.primaryContainer
border.color: Theme.primary
border.width: 1
StyledText {
anchors.centerIn: parent
text: "100"
font.pixelSize: 9
font.weight: Font.Medium
color: (widgetData?.width || 50) === 100 ? Theme.primaryText : Theme.primary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = 100
SettingsData.setControlCenterWidgets(widgets)
}
}
}
}
}
// Arrow buttons for reordering in top-left
Row {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 4
spacing: 2
visible: editMode
z: 20
Rectangle {
width: 16
height: 16
radius: 8
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
DankIcon {
anchors.centerIn: parent
name: "keyboard_arrow_left"
size: 12
color: Theme.surfaceText
}
MouseArea {
anchors.fill: parent
enabled: widgetIndex > 0
opacity: enabled ? 1.0 : 0.5
onClicked: root.moveWidget(widgetIndex, widgetIndex - 1)
}
}
Rectangle {
width: 16
height: 16
radius: 8
color: Theme.surfaceContainer
border.color: Theme.outline
border.width: 1
DankIcon {
anchors.centerIn: parent
name: "keyboard_arrow_right"
size: 12
color: Theme.surfaceText
}
MouseArea {
anchors.fill: parent
enabled: widgetIndex < ((SettingsData.controlCenterWidgets?.length ?? 0) - 1)
opacity: enabled ? 1.0 : 0.5
onClicked: root.moveWidget(widgetIndex, widgetIndex + 1)
}
}
}
// Border highlight
Rectangle {
anchors.fill: parent
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
radius: Theme.cornerRadius
border.color: Theme.primary
border.width: editMode ? 1 : 0
visible: editMode
z: -1
Behavior on border.width {
NumberAnimation { duration: Theme.shortDuration }
}
}
}

View File

@@ -6,9 +6,10 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool powerOptionsExpanded: false
property bool editMode: false property bool editMode: false
signal powerButtonClicked() signal powerActionRequested(string action, string title, string message)
signal lockRequested() signal lockRequested()
signal editModeToggled() signal editModeToggled()
@@ -17,7 +18,7 @@ Rectangle {
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08) Theme.outline.b, 0.08)
border.width: 0 border.width: 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -82,11 +83,13 @@ Rectangle {
DankActionButton { DankActionButton {
buttonSize: 36 buttonSize: 36
iconName: "power_settings_new" iconName: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
iconSize: Theme.iconSize - 4 iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText iconColor: root.powerOptionsExpanded ? Theme.primary : Theme.surfaceText
backgroundColor: "transparent" backgroundColor: "transparent"
onClicked: root.powerButtonClicked() onClicked: {
root.powerOptionsExpanded = !root.powerOptionsExpanded
}
} }
DankActionButton { DankActionButton {

View File

@@ -0,0 +1,70 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
property bool expanded: false
signal powerActionRequested(string action, string title, string message)
implicitHeight: expanded ? 60 : 0
height: implicitHeight
clip: true
Rectangle {
width: parent.width
height: 60
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: root.expanded ? 1 : 0
opacity: root.expanded ? 1 : 0
clip: true
Row {
anchors.centerIn: parent
spacing: SessionService.hibernateSupported ? Theme.spacingS : Theme.spacingL
visible: root.expanded
PowerButton {
width: SessionService.hibernateSupported ? 85 : 100
iconName: "logout"
text: "Logout"
onPressed: root.powerActionRequested("logout", "Logout", "Are you sure you want to logout?")
}
PowerButton {
width: SessionService.hibernateSupported ? 85 : 100
iconName: "restart_alt"
text: "Restart"
onPressed: root.powerActionRequested("reboot", "Restart", "Are you sure you want to restart?")
}
PowerButton {
width: SessionService.hibernateSupported ? 85 : 100
iconName: "bedtime"
text: "Suspend"
onPressed: root.powerActionRequested("suspend", "Suspend", "Are you sure you want to suspend?")
}
PowerButton {
width: SessionService.hibernateSupported ? 85 : 100
iconName: "ac_unit"
text: "Hibernate"
visible: SessionService.hibernateSupported
onPressed: root.powerActionRequested("hibernate", "Hibernate", "Are you sure you want to hibernate?")
}
PowerButton {
width: SessionService.hibernateSupported ? 85 : 100
iconName: "power_settings_new"
text: "Shutdown"
onPressed: root.powerActionRequested("poweroff", "Shutdown", "Are you sure you want to shutdown?")
}
}
}
}

View File

@@ -1,52 +0,0 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Widgets
Row {
id: root
property int currentSize: 50
property bool isSlider: false
property int widgetIndex: -1
signal sizeChanged(int newSize)
readonly property var availableSizes: isSlider ? [50, 100] : [25, 50, 75, 100]
spacing: 2
Repeater {
model: root.availableSizes
Rectangle {
width: 16
height: 16
radius: 3
color: modelData === root.currentSize ? Theme.primary : Theme.surfaceContainer
border.color: modelData === root.currentSize ? Theme.primary : Theme.outline
border.width: 1
StyledText {
anchors.centerIn: parent
text: modelData.toString()
font.pixelSize: 8
font.weight: Font.Medium
color: modelData === root.currentSize ? Theme.primaryText : Theme.surfaceText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.currentSize = modelData
root.sizeChanged(modelData)
}
}
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
}
}
}

View File

@@ -13,8 +13,6 @@ Column {
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var model: null property var model: null
property var expandedWidgetData: null property var expandedWidgetData: null
property var bluetoothCodecSelector: null
property bool darkModeTransitionPending: false
signal expandClicked(var widgetData, int globalIndex) signal expandClicked(var widgetData, int globalIndex)
signal removeWidget(int index) signal removeWidget(int index)
@@ -26,7 +24,6 @@ Column {
property var currentRowWidgets: [] property var currentRowWidgets: []
property real currentRowWidth: 0 property real currentRowWidth: 0
property int expandedRowIndex: -1 property int expandedRowIndex: -1
property var colorPickerModal: null
function calculateRowsAndWidgets() { function calculateRowsAndWidgets() {
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex) return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
@@ -41,17 +38,6 @@ Column {
expandedRowIndex = layoutResult.expandedRowIndex expandedRowIndex = layoutResult.expandedRowIndex
} }
function moveToTop(item) {
const children = root.children
for (var i = 0; i < children.length; i++) {
if (children[i] === item)
continue
if (children[i].z)
children[i].z = Math.min(children[i].z, 999)
}
item.z = 1000
}
Repeater { Repeater {
model: root.layoutResult.rows model: root.layoutResult.rows
@@ -65,8 +51,8 @@ Column {
if (widgets.length === 0) return false if (widgets.length === 0) return false
return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider") return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider")
} }
topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0 topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0 bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -12) : 0
Flow { Flow {
width: parent.width width: parent.width
@@ -75,8 +61,8 @@ Column {
Repeater { Repeater {
model: rowWidgets || [] model: rowWidgets || []
DragDropWidgetWrapper { Item {
widgetData: modelData property var widgetData: modelData
property int globalWidgetIndex: { property int globalWidgetIndex: {
const widgets = SettingsData.controlCenterWidgets || [] const widgets = SettingsData.controlCenterWidgets || []
for (var i = 0; i < widgets.length; i++) { for (var i = 0; i < widgets.length; i++) {
@@ -106,43 +92,36 @@ Column {
return baseWidth return baseWidth
} }
} }
height: isSliderOnlyRow ? 48 : 60 height: 60
editMode: root.editMode Loader {
widgetIndex: globalWidgetIndex id: widgetLoader
gridCellWidth: width anchors.fill: parent
gridCellHeight: height property var widgetData: parent.widgetData
gridColumns: 4 property int widgetIndex: parent.globalWidgetIndex
gridLayout: root property int globalWidgetIndex: parent.globalWidgetIndex
isSlider: { property int widgetWidth: parent.widgetWidth
const id = modelData.id || ""
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider"
}
widgetComponent: { sourceComponent: {
const id = modelData.id || "" const id = modelData.id || ""
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") { if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
return compoundPillComponent return compoundPillComponent
} else if (id === "volumeSlider") { } else if (id === "volumeSlider") {
return audioSliderComponent return audioSliderComponent
} else if (id === "brightnessSlider") { } else if (id === "brightnessSlider") {
return brightnessSliderComponent return brightnessSliderComponent
} else if (id === "inputVolumeSlider") { } else if (id === "inputVolumeSlider") {
return inputAudioSliderComponent return inputAudioSliderComponent
} else if (id === "battery") { } else if (id === "battery") {
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
} else if (id === "diskUsage") { } else if (id === "diskUsage") {
return diskUsagePillComponent return diskUsagePillComponent
} else if (id === "colorPicker") { } else {
return colorPickerPillComponent return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
} else { }
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
} }
}
onWidgetMoved: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex) }
onRemoveWidget: index => root.removeWidget(index)
onToggleWidgetSize: index => root.toggleWidgetSize(index)
} }
} }
} }
@@ -163,7 +142,6 @@ Column {
visible: active visible: active
expandedSection: root.expandedSection expandedSection: root.expandedSection
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
bluetoothCodecSelector: root.bluetoothCodecSelector
} }
} }
} }
@@ -178,198 +156,201 @@ Column {
height: 60 height: 60
iconName: { iconName: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi": {
{ if (NetworkService.wifiToggling) {
if (NetworkService.wifiToggling)
return "sync" return "sync"
if (NetworkService.networkStatus === "ethernet") }
if (NetworkService.networkStatus === "ethernet") {
return "settings_ethernet" return "settings_ethernet"
if (NetworkService.networkStatus === "wifi") }
if (NetworkService.networkStatus === "wifi") {
return NetworkService.wifiSignalIcon return NetworkService.wifiSignalIcon
if (NetworkService.wifiEnabled) }
if (NetworkService.wifiEnabled) {
return "wifi_off" return "wifi_off"
}
return "wifi_off" return "wifi_off"
} }
case "bluetooth": case "bluetooth": {
{ if (!BluetoothService.available) {
if (!BluetoothService.available)
return "bluetooth_disabled" return "bluetooth_disabled"
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) }
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
return "bluetooth_disabled" return "bluetooth_disabled"
}
const primaryDevice = (() => { const primaryDevice = (() => {
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
return null return null
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))] }
for (let device of devices) { let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
if (device && device.connected) for (let device of devices) {
return device if (device && device.connected) {
} return device
return null }
})() }
if (primaryDevice) return null
})()
if (primaryDevice) {
return BluetoothService.getDeviceIcon(primaryDevice) return BluetoothService.getDeviceIcon(primaryDevice)
}
return "bluetooth" return "bluetooth"
} }
case "audioOutput": case "audioOutput": {
{ if (!AudioService.sink) return "volume_off"
if (!AudioService.sink)
return "volume_off"
let volume = AudioService.sink.audio.volume let volume = AudioService.sink.audio.volume
let muted = AudioService.sink.audio.muted let muted = AudioService.sink.audio.muted
if (muted || volume === 0.0) if (muted || volume === 0.0) return "volume_off"
return "volume_off" if (volume <= 0.33) return "volume_down"
if (volume <= 0.33) if (volume <= 0.66) return "volume_up"
return "volume_down"
if (volume <= 0.66)
return "volume_up"
return "volume_up" return "volume_up"
} }
case "audioInput": case "audioInput": {
{ if (!AudioService.source) return "mic_off"
if (!AudioService.source)
return "mic_off"
let muted = AudioService.source.audio.muted let muted = AudioService.source.audio.muted
return muted ? "mic_off" : "mic" return muted ? "mic_off" : "mic"
} }
default: default: return widgetDef?.icon || "help"
return widgetDef?.icon || "help"
} }
} }
primaryText: { primaryText: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi": {
{ if (NetworkService.wifiToggling) {
if (NetworkService.wifiToggling)
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..." return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
if (NetworkService.networkStatus === "ethernet") }
if (NetworkService.networkStatus === "ethernet") {
return "Ethernet" return "Ethernet"
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) }
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID) {
return NetworkService.currentWifiSSID return NetworkService.currentWifiSSID
if (NetworkService.wifiEnabled) }
if (NetworkService.wifiEnabled) {
return "Not connected" return "Not connected"
}
return "WiFi off" return "WiFi off"
} }
case "bluetooth": case "bluetooth": {
{ if (!BluetoothService.available) {
if (!BluetoothService.available)
return "Bluetooth" return "Bluetooth"
if (!BluetoothService.adapter) }
if (!BluetoothService.adapter) {
return "No adapter" return "No adapter"
if (!BluetoothService.adapter.enabled) }
if (!BluetoothService.adapter.enabled) {
return "Disabled" return "Disabled"
}
return "Enabled" return "Enabled"
} }
case "audioOutput": case "audioOutput": return AudioService.sink?.description || "No output device"
return AudioService.sink?.description || "No output device" case "audioInput": return AudioService.source?.description || "No input device"
case "audioInput": default: return widgetDef?.text || "Unknown"
return AudioService.source?.description || "No input device"
default:
return widgetDef?.text || "Unknown"
} }
} }
secondaryText: { secondaryText: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi": {
{ if (NetworkService.wifiToggling) {
if (NetworkService.wifiToggling)
return "Please wait..." return "Please wait..."
if (NetworkService.networkStatus === "ethernet") }
if (NetworkService.networkStatus === "ethernet") {
return "Connected" return "Connected"
if (NetworkService.networkStatus === "wifi") }
if (NetworkService.networkStatus === "wifi") {
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected" return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
if (NetworkService.wifiEnabled) }
if (NetworkService.wifiEnabled) {
return "Select network" return "Select network"
}
return "" return ""
} }
case "bluetooth": case "bluetooth": {
{ if (!BluetoothService.available) {
if (!BluetoothService.available)
return "No adapters" return "No adapters"
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) }
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
return "Off" return "Off"
}
const primaryDevice = (() => { const primaryDevice = (() => {
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
return null return null
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))] }
for (let device of devices) { let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
if (device && device.connected) for (let device of devices) {
return device if (device && device.connected) {
} return device
return null }
})() }
if (primaryDevice) return null
})()
if (primaryDevice) {
return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device" return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device"
}
return "No devices" return "No devices"
} }
case "audioOutput": case "audioOutput": {
{ if (!AudioService.sink) {
if (!AudioService.sink)
return "Select device" return "Select device"
if (AudioService.sink.audio.muted) }
if (AudioService.sink.audio.muted) {
return "Muted" return "Muted"
}
return Math.round(AudioService.sink.audio.volume * 100) + "%" return Math.round(AudioService.sink.audio.volume * 100) + "%"
} }
case "audioInput": case "audioInput": {
{ if (!AudioService.source) {
if (!AudioService.source)
return "Select device" return "Select device"
if (AudioService.source.audio.muted) }
if (AudioService.source.audio.muted) {
return "Muted" return "Muted"
}
return Math.round(AudioService.source.audio.volume * 100) + "%" return Math.round(AudioService.source.audio.volume * 100) + "%"
} }
default: default: return widgetDef?.description || ""
return widgetDef?.description || ""
} }
} }
isActive: { isActive: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi": {
{ if (NetworkService.wifiToggling) {
if (NetworkService.wifiToggling)
return false return false
if (NetworkService.networkStatus === "ethernet") }
if (NetworkService.networkStatus === "ethernet") {
return true return true
if (NetworkService.networkStatus === "wifi") }
if (NetworkService.networkStatus === "wifi") {
return true return true
}
return NetworkService.wifiEnabled return NetworkService.wifiEnabled
} }
case "bluetooth": case "bluetooth": return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled) case "audioOutput": return !!(AudioService.sink && !AudioService.sink.audio.muted)
case "audioOutput": case "audioInput": return !!(AudioService.source && !AudioService.source.audio.muted)
return !!(AudioService.sink && !AudioService.sink.audio.muted) default: return false
case "audioInput":
return !!(AudioService.source && !AudioService.source.audio.muted)
default:
return false
} }
} }
enabled: widgetDef?.enabled ?? true enabled: (widgetDef?.enabled ?? true)
onToggled: { onToggled: {
if (root.editMode) return if (root.editMode) return
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi": {
{
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) { if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
NetworkService.toggleWifiRadio() NetworkService.toggleWifiRadio()
} }
break break
} }
case "bluetooth": case "bluetooth": {
{
if (BluetoothService.available && BluetoothService.adapter) { if (BluetoothService.available && BluetoothService.adapter) {
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
} }
break break
} }
case "audioOutput": case "audioOutput": {
{
if (AudioService.sink && AudioService.sink.audio) { if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = !AudioService.sink.audio.muted AudioService.sink.audio.muted = !AudioService.sink.audio.muted
} }
break break
} }
case "audioInput": case "audioInput": {
{
if (AudioService.source && AudioService.source.audio) { if (AudioService.source && AudioService.source.audio) {
AudioService.source.audio.muted = !AudioService.source.audio.muted AudioService.source.audio.muted = !AudioService.source.audio.muted
} }
@@ -382,11 +363,9 @@ Column {
root.expandClicked(widgetData, widgetIndex) root.expandClicked(widgetData, widgetIndex)
} }
onWheelEvent: function (wheelEvent) { onWheelEvent: function (wheelEvent) {
if (root.editMode) return
const id = widgetData.id || "" const id = widgetData.id || ""
if (id === "audioOutput") { if (id === "audioOutput") {
if (!AudioService.sink || !AudioService.sink.audio) if (!AudioService.sink || !AudioService.sink.audio) return
return
let delta = wheelEvent.angleDelta.y let delta = wheelEvent.angleDelta.y
let currentVolume = AudioService.sink.audio.volume * 100 let currentVolume = AudioService.sink.audio.volume * 100
let newVolume let newVolume
@@ -398,8 +377,7 @@ Column {
AudioService.sink.audio.volume = newVolume / 100 AudioService.sink.audio.volume = newVolume / 100
wheelEvent.accepted = true wheelEvent.accepted = true
} else if (id === "audioInput") { } else if (id === "audioInput") {
if (!AudioService.source || !AudioService.source.audio) if (!AudioService.source || !AudioService.source.audio) return
return
let delta = wheelEvent.angleDelta.y let delta = wheelEvent.angleDelta.y
let currentVolume = AudioService.source.audio.volume * 100 let currentVolume = AudioService.source.audio.volume * 100
let newVolume let newVolume
@@ -412,6 +390,18 @@ Column {
wheelEvent.accepted = true wheelEvent.accepted = true
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -420,6 +410,7 @@ Column {
Item { Item {
property var widgetData: parent.widgetData || {} property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0 property int widgetIndex: parent.widgetIndex || 0
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
width: parent.width width: parent.width
height: 16 height: 16
@@ -429,6 +420,18 @@ Column {
height: 14 height: 14
property color sliderTrackColor: Theme.surfaceContainerHigh property color sliderTrackColor: Theme.surfaceContainerHigh
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: true
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -446,6 +449,18 @@ Column {
height: 14 height: 14
property color sliderTrackColor: Theme.surfaceContainerHigh property color sliderTrackColor: Theme.surfaceContainerHigh
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: true
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -463,6 +478,18 @@ Column {
height: 14 height: 14
property color sliderTrackColor: Theme.surfaceContainerHigh property color sliderTrackColor: Theme.surfaceContainerHigh
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: true
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -479,6 +506,18 @@ Column {
root.expandClicked(widgetData, widgetIndex) root.expandClicked(widgetData, widgetIndex)
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -495,6 +534,18 @@ Column {
root.expandClicked(widgetData, widgetIndex) root.expandClicked(widgetData, widgetIndex)
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -503,99 +554,80 @@ Column {
ToggleButton { ToggleButton {
property var widgetData: parent.widgetData || {} property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0 property int widgetIndex: parent.widgetIndex || 0
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
width: parent.width width: parent.width
height: 60 height: 60
iconName: { iconName: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode" case "darkMode": return "contrast"
case "darkMode": case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
return "contrast" case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
case "doNotDisturb": default: return widgetDef?.icon || "help"
return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
case "idleInhibitor":
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
default:
return "help"
} }
} }
text: { text: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": return "Night Mode"
return "Night Mode" case "darkMode": return SessionData.isLightMode ? "Light Mode" : "Dark Mode"
case "darkMode": case "doNotDisturb": return "Do Not Disturb"
return SessionData.isLightMode ? "Light Mode" : "Dark Mode" case "idleInhibitor": return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
case "doNotDisturb": default: return widgetDef?.text || "Unknown"
return "Do Not Disturb"
case "idleInhibitor":
return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
default:
return "Unknown"
} }
} }
iconRotation: { secondaryText: ""
if (widgetData.id !== "darkMode") return 0
if (darkModeTransitionPending) { iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
return SessionData.isLightMode ? 0 : 180
}
return SessionData.isLightMode ? 180 : 0
}
isActive: { isActive: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": return DisplayService.nightModeEnabled || false
return DisplayService.nightModeEnabled || false case "darkMode": return !SessionData.isLightMode
case "darkMode": case "doNotDisturb": return SessionData.doNotDisturb || false
return !SessionData.isLightMode case "idleInhibitor": return SessionService.idleInhibited || false
case "doNotDisturb": default: return false
return SessionData.doNotDisturb || false
case "idleInhibitor":
return SessionService.idleInhibited || false
default:
return false
} }
} }
enabled: !root.editMode enabled: (widgetDef?.enabled ?? true) && !root.editMode
onIconRotationCompleted: {
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
root.darkModeTransitionPending = false
Theme.screenTransition()
Theme.toggleLightMode()
}
}
onClicked: { onClicked: {
if (root.editMode)
return
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": {
{ if (DisplayService.automationAvailable) {
if (DisplayService.automationAvailable)
DisplayService.toggleNightMode() DisplayService.toggleNightMode()
}
break break
} }
case "darkMode": case "darkMode": {
{ Theme.toggleLightMode()
root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb": {
{
SessionData.setDoNotDisturb(!SessionData.doNotDisturb) SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
break break
} }
case "idleInhibitor": case "idleInhibitor": {
{
SessionService.toggleIdleInhibit() SessionService.toggleIdleInhibit()
break break
} }
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -604,84 +636,68 @@ enabled: !root.editMode
SmallToggleButton { SmallToggleButton {
property var widgetData: parent.widgetData || {} property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0 property int widgetIndex: parent.widgetIndex || 0
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
width: parent.width width: parent.width
height: 48 height: 48
iconName: { iconName: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode" case "darkMode": return "contrast"
case "darkMode": case "doNotDisturb": return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
return "contrast" case "idleInhibitor": return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
case "doNotDisturb": default: return widgetDef?.icon || "help"
return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
case "idleInhibitor":
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
default:
return "help"
} }
} }
iconRotation: { iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0
if (widgetData.id !== "darkMode") return 0
if (darkModeTransitionPending) {
return SessionData.isLightMode ? 0 : 180
}
return SessionData.isLightMode ? 180 : 0
}
isActive: { isActive: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": return DisplayService.nightModeEnabled || false
return DisplayService.nightModeEnabled || false case "darkMode": return !SessionData.isLightMode
case "darkMode": case "doNotDisturb": return SessionData.doNotDisturb || false
return !SessionData.isLightMode case "idleInhibitor": return SessionService.idleInhibited || false
case "doNotDisturb": default: return false
return SessionData.doNotDisturb || false
case "idleInhibitor":
return SessionService.idleInhibited || false
default:
return false
} }
} }
enabled: !root.editMode enabled: (widgetDef?.enabled ?? true) && !root.editMode
onIconRotationCompleted: {
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
root.darkModeTransitionPending = false
Theme.screenTransition()
Theme.toggleLightMode()
}
}
onClicked: { onClicked: {
if (root.editMode)
return
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "nightMode": case "nightMode": {
{ if (DisplayService.automationAvailable) {
if (DisplayService.automationAvailable)
DisplayService.toggleNightMode() DisplayService.toggleNightMode()
}
break break
} }
case "darkMode": case "darkMode": {
{ Theme.toggleLightMode()
root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb": {
{
SessionData.setDoNotDisturb(!SessionData.doNotDisturb) SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
break break
} }
case "idleInhibitor": case "idleInhibitor": {
{
SessionService.toggleIdleInhibit() SessionService.toggleIdleInhibit()
break break
} }
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
@@ -701,18 +717,18 @@ enabled: !root.editMode
root.expandClicked(widgetData, widgetIndex) root.expandClicked(widgetData, widgetIndex)
} }
} }
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
} }
} }
}
Component {
id: colorPickerPillComponent
ColorPickerPill {
property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0
width: parent.width
height: 60
colorPickerModal: root.colorPickerModal
}
}
}

View File

@@ -10,7 +10,7 @@ import qs.Common
import qs.Modules.ControlCenter import qs.Modules.ControlCenter
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
import qs.Modules.ControlCenter.Details import qs.Modules.ControlCenter.Details
import qs.Modules.DankBar import qs.Modules.TopBar
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Components import qs.Modules.ControlCenter.Components
@@ -21,11 +21,14 @@ DankPopout {
id: root id: root
property string expandedSection: "" property string expandedSection: ""
property bool powerOptionsExpanded: false
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
property bool editMode: false property bool editMode: false
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var expandedWidgetData: null property var expandedWidgetData: null
signal powerActionRequested(string action, string title, string message)
signal lockRequested signal lockRequested
function collapseAll() { function collapseAll() {
@@ -63,9 +66,9 @@ DankPopout {
popupWidth: 550 popupWidth: 550
popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400) popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400)
triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 80 triggerWidth: 80
positioning: "" positioning: "center"
screen: triggerScreen screen: triggerScreen
shouldBeVisible: false shouldBeVisible: false
visible: shouldBeVisible visible: shouldBeVisible
@@ -99,14 +102,14 @@ DankPopout {
property alias bluetoothCodecSelector: bluetoothCodecSelector property alias bluetoothCodecSelector: bluetoothCodecSelector
color: { color: {
const transparency = Theme.popupTransparency const transparency = Theme.popupTransparency || 0.92
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
return Qt.rgba(surface.r, surface.g, surface.b, transparency) return Qt.rgba(surface.r, surface.g, surface.b, transparency)
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08) Theme.outline.b, 0.08)
border.width: 0 border.width: 1
antialiasing: true antialiasing: true
smooth: true smooth: true
@@ -120,25 +123,29 @@ DankPopout {
HeaderPane { HeaderPane {
id: headerPane id: headerPane
width: parent.width width: parent.width
powerOptionsExpanded: root.powerOptionsExpanded
editMode: root.editMode editMode: root.editMode
onPowerOptionsExpandedChanged: root.powerOptionsExpanded = powerOptionsExpanded
onEditModeToggled: root.editMode = !root.editMode onEditModeToggled: root.editMode = !root.editMode
onPowerButtonClicked: { onPowerActionRequested: (action, title, message) => root.powerActionRequested(action, title, message)
if (powerMenuModalLoader) {
powerMenuModalLoader.active = true
if (powerMenuModalLoader.item) {
const popoutPos = controlContent.mapToItem(null, 0, 0)
const bounds = Qt.rect(popoutPos.x, popoutPos.y, controlContent.width, controlContent.height)
powerMenuModalLoader.item.openFromControlCenter(bounds, root.triggerScreen)
}
}
}
onLockRequested: { onLockRequested: {
root.close() root.close()
root.lockRequested() root.lockRequested()
} }
} }
DragDropGrid { PowerOptionsPane {
id: powerOptionsPane
width: parent.width
expanded: root.powerOptionsExpanded
onPowerActionRequested: (action, title, message) => {
root.powerOptionsExpanded = false
root.close()
root.powerActionRequested(action, title, message)
}
}
WidgetGrid {
id: widgetGrid id: widgetGrid
width: parent.width width: parent.width
editMode: root.editMode editMode: root.editMode
@@ -146,8 +153,6 @@ DankPopout {
expandedWidgetIndex: root.expandedWidgetIndex expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
model: widgetModel model: widgetModel
bluetoothCodecSelector: bluetoothCodecSelector
colorPickerModal: root.colorPickerModal
onExpandClicked: (widgetData, globalIndex) => { onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex root.expandedWidgetIndex = globalIndex
root.expandedWidgetData = widgetData root.expandedWidgetData = widgetData
@@ -165,7 +170,6 @@ DankPopout {
EditControls { EditControls {
width: parent.width width: parent.width
visible: editMode visible: editMode
popoutContent: controlContent
availableWidgets: { availableWidgets: {
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id) const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id)) return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id))
@@ -218,7 +222,4 @@ DankPopout {
id: batteryDetailComponent id: batteryDetailComponent
BatteryDetail {} BatteryDetail {}
} }
property var colorPickerModal: null
property var powerMenuModalLoader: null
} }

View File

@@ -16,7 +16,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
Row { Row {
id: headerRow id: headerRow
@@ -57,6 +57,10 @@ Rectangle {
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
anchors.fill: parent anchors.fill: parent
@@ -134,9 +138,9 @@ Rectangle {
width: parent.width width: parent.width
height: 50 height: 50
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHigh
border.color: modelData === AudioService.source ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: modelData === AudioService.source ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 0 border.width: modelData === AudioService.source ? 2 : 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -194,6 +198,14 @@ Rectangle {
} }
} }
} }
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
} }

View File

@@ -16,7 +16,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
Row { Row {
id: headerRow id: headerRow
@@ -57,6 +57,10 @@ Rectangle {
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
anchors.fill: parent anchors.fill: parent
@@ -139,9 +143,9 @@ Rectangle {
width: parent.width width: parent.width
height: 50 height: 50
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHigh
border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 0 border.width: modelData === AudioService.sink ? 2 : 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -201,6 +205,14 @@ Rectangle {
} }
} }
} }
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
} }

View File

@@ -11,7 +11,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
function isActiveProfile(profile) { function isActiveProfile(profile) {
if (typeof PowerProfiles === "undefined") { if (typeof PowerProfiles === "undefined") {
@@ -209,7 +209,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3) border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
border.width: 0 border.width: 1
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
Column { Column {

View File

@@ -124,7 +124,7 @@ Item {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainer color: Theme.surfaceContainer
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 border.width: 1
opacity: modalVisible ? 1 : 0 opacity: modalVisible ? 1 : 0
scale: modalVisible ? 1 : 0.9 scale: modalVisible ? 1 : 0.9
@@ -206,14 +206,14 @@ Item {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (modelData.name === currentCodec) if (modelData.name === currentCodec)
return Theme.surfaceContainerHighest; return Theme.surfaceContainerHigh;
else if (codecMouseArea.containsMouse) else if (codecMouseArea.containsMouse)
return Theme.surfaceHover; return Theme.surfaceHover;
else else
return "transparent"; return "transparent";
} }
border.color: "transparent" border.color: "transparent"
border.width: 0 border.width: 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -272,6 +272,12 @@ Item {
} }
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
} }

View File

@@ -11,7 +11,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
property var bluetoothCodecModalRef: null property var bluetoothCodecModalRef: null
@@ -62,7 +62,7 @@ Rectangle {
return scanMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent" return scanMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
} }
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 0 border.width: 1
visible: BluetoothService.adapter && BluetoothService.adapter.enabled visible: BluetoothService.adapter && BluetoothService.adapter.enabled
Row { Row {
@@ -96,6 +96,13 @@ Rectangle {
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
} }
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }
} }
@@ -152,7 +159,7 @@ Rectangle {
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)
if (deviceMouseArea.containsMouse) if (deviceMouseArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
return Theme.surfaceContainerHighest return Theme.surfaceContainerHigh
} }
border.color: { border.color: {
if (modelData.state === BluetoothDeviceState.Connecting) if (modelData.state === BluetoothDeviceState.Connecting)
@@ -161,7 +168,7 @@ Rectangle {
return Theme.primary return Theme.primary
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
} }
border.width: 0 border.width: (modelData.connected || modelData.state === BluetoothDeviceState.Connecting) ? 2 : 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -277,6 +284,14 @@ Rectangle {
} }
} }
} }
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
@@ -332,9 +347,9 @@ Rectangle {
width: parent.width width: parent.width
height: 50 height: 50
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHigh
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: 0 border.width: 1
opacity: canConnect ? 1 : 0.6 opacity: canConnect ? 1 : 0.6
Row { Row {
@@ -412,6 +427,9 @@ Rectangle {
} }
} }
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
@@ -440,7 +458,7 @@ Rectangle {
background: Rectangle { background: Rectangle {
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 0 border.width: 1
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)
} }

View File

@@ -6,8 +6,6 @@ import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { Rectangle {
id: root
property string currentMountPath: "/" property string currentMountPath: "/"
property string instanceId: "" property string instanceId: ""
@@ -17,7 +15,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
Component.onCompleted: { Component.onCompleted: {
DgopService.addRef(["diskmounts"]) DgopService.addRef(["diskmounts"])
@@ -78,9 +76,9 @@ Rectangle {
width: parent.width width: parent.width
height: 80 height: 80
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHighest color: Theme.surfaceContainerHigh
border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: modelData.mount === currentMountPath ? 2 : 0 border.width: modelData.mount === currentMountPath ? 2 : 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -154,11 +152,16 @@ Rectangle {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
currentMountPath = modelData.mount if (modelData.mount !== currentMountPath) {
mountPathChanged(modelData.mount) currentMountPath = modelData.mount
mountPathChanged(modelData.mount)
}
} }
} }
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
} }

View File

@@ -19,7 +19,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
Component.onCompleted: { Component.onCompleted: {
NetworkService.addRef() NetworkService.addRef()
@@ -27,11 +27,20 @@ Rectangle {
NetworkService.scanWifi() NetworkService.scanWifi()
} }
} }
Component.onDestruction: { Component.onDestruction: {
NetworkService.removeRef() NetworkService.removeRef()
} }
property var wifiPasswordModalRef: {
wifiPasswordModalLoader.active = true
return wifiPasswordModalLoader.item
}
property var networkInfoModalRef: {
networkInfoModalLoader.active = true
return networkInfoModalLoader.item
}
Row { Row {
id: headerRow id: headerRow
anchors.left: parent.left anchors.left: parent.left
@@ -149,7 +158,7 @@ Rectangle {
height: 36 height: 36
radius: 18 radius: 18
color: enableWifiButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) color: enableWifiButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
border.width: 0 border.width: 1
border.color: Theme.primary border.color: Theme.primary
StyledText { StyledText {
@@ -168,6 +177,12 @@ Rectangle {
onClicked: NetworkService.toggleWifiRadio() onClicked: NetworkService.toggleWifiRadio()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }
} }
} }
@@ -227,9 +242,9 @@ Rectangle {
width: parent.width width: parent.width
height: 50 height: 50
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHigh
border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 0 border.width: modelData.ssid === NetworkService.currentWifiSSID ? 2 : 1
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@@ -317,7 +332,9 @@ Rectangle {
onClicked: function(event) { onClicked: function(event) {
if (modelData.ssid !== NetworkService.currentWifiSSID) { if (modelData.ssid !== NetworkService.currentWifiSSID) {
if (modelData.secured && !modelData.saved) { if (modelData.secured && !modelData.saved) {
wifiPasswordModal.show(modelData.ssid) if (wifiPasswordModalRef) {
wifiPasswordModalRef.show(modelData.ssid)
}
} else { } else {
NetworkService.connectToWifi(modelData.ssid) NetworkService.connectToWifi(modelData.ssid)
} }
@@ -326,6 +343,13 @@ Rectangle {
} }
} }
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
} }
} }
} }
@@ -345,7 +369,7 @@ Rectangle {
background: Rectangle { background: Rectangle {
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 0 border.width: 1
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)
} }
@@ -371,7 +395,9 @@ Rectangle {
NetworkService.disconnectWifi() NetworkService.disconnectWifi()
} else { } else {
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) { if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
wifiPasswordModal.show(networkContextMenu.currentSSID) if (wifiPasswordModalRef) {
wifiPasswordModalRef.show(networkContextMenu.currentSSID)
}
} else { } else {
NetworkService.connectToWifi(networkContextMenu.currentSSID) NetworkService.connectToWifi(networkContextMenu.currentSSID)
} }
@@ -397,8 +423,10 @@ Rectangle {
} }
onTriggered: { onTriggered: {
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID) if (networkInfoModalRef) {
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData) let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
networkInfoModalRef.showNetworkInfo(networkContextMenu.currentSSID, networkData)
}
} }
} }
@@ -426,12 +454,22 @@ Rectangle {
} }
} }
WifiPasswordModal { LazyLoader {
id: wifiPasswordModal id: wifiPasswordModalLoader
active: false
WifiPasswordModal {
id: wifiPasswordModal
}
} }
NetworkInfoModal { LazyLoader {
id: networkInfoModal id: networkInfoModalLoader
active: false
NetworkInfoModal {
id: networkInfoModal
}
} }

View File

@@ -116,14 +116,6 @@ QtObject {
"enabled": DgopService.dgopAvailable, "enabled": DgopService.dgopAvailable,
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined, "warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
"allowMultiple": true "allowMultiple": true
},
{
"id": "colorPicker",
"text": "Color Picker",
"description": "Choose colors from palette",
"icon": "palette",
"type": "action",
"enabled": true
} }
] ]

View File

@@ -44,7 +44,7 @@ PanelWindow {
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08) Theme.outline.b, 0.08)
border.width: 0 border.width: 1
opacity: powerMenuVisible ? 1 : 0 opacity: powerMenuVisible ? 1 : 0
scale: powerMenuVisible ? 1 : 0.85 scale: powerMenuVisible ? 1 : 0.85

View File

@@ -20,7 +20,11 @@ Row {
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
@@ -30,9 +34,7 @@ Row {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (defaultSink) { if (defaultSink) {
AudioService.suppressOSD = true
defaultSink.audio.muted = !defaultSink.audio.muted defaultSink.audio.muted = !defaultSink.audio.muted
AudioService.suppressOSD = false
} }
} }
} }
@@ -69,9 +71,6 @@ Row {
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
onIsDraggingChanged: {
AudioService.suppressOSD = isDragging
}
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (defaultSink) { if (defaultSink) {
defaultSink.audio.volume = newValue / 100.0 defaultSink.audio.volume = newValue / 100.0

View File

@@ -30,6 +30,9 @@ CompoundPill {
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) { if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
return "bluetooth_disabled" return "bluetooth_disabled"
} }
if (primaryDevice) {
return BluetoothService.getDeviceIcon(primaryDevice)
}
return "bluetooth" return "bluetooth"
} }

View File

@@ -18,13 +18,17 @@ Row {
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse color: iconArea.containsMouse
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
: Theme.withAlpha(Theme.primary, 0) : "transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: Qt.PointingHandCursor
onClicked: function(event) { onClicked: function(event) {
if (DisplayService.devices.length > 1) { if (DisplayService.devices.length > 1) {
@@ -37,22 +41,6 @@ Row {
} }
} }
onEntered: {
tooltipLoader.active = true
if (tooltipLoader.item) {
const tooltipText = DisplayService.currentDevice ? "bl device: " + DisplayService.currentDevice : "Backlight Control"
const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
}
tooltipLoader.active = false
}
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
@@ -103,7 +91,7 @@ Row {
background: Rectangle { background: Rectangle {
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 0 border.width: 1
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)
} }
@@ -153,10 +141,4 @@ Row {
onObjectRemoved: (index, object) => deviceMenu.removeItem(object) onObjectRemoved: (index, object) => deviceMenu.removeItem(object)
} }
} }
Loader {
id: tooltipLoader
active: false
sourceComponent: DankTooltip {}
}
} }

View File

@@ -1,33 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
CompoundPill {
id: root
property var colorPickerModal: null
isActive: true
iconName: "palette"
iconColor: Theme.primary
primaryText: "Color Picker"
secondaryText: "Choose a color"
onToggled: {
console.log("ColorPickerPill toggled, modal:", colorPickerModal)
if (colorPickerModal) {
colorPickerModal.show()
}
}
onExpandClicked: {
console.log("ColorPickerPill expandClicked, modal:", colorPickerModal)
if (colorPickerModal) {
colorPickerModal.show()
}
}
}

View File

@@ -22,7 +22,7 @@ Rectangle {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
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 border.width: 1
opacity: enabled ? 1.0 : 0.6 opacity: enabled ? 1.0 : 0.6
Row { Row {

View File

@@ -29,19 +29,16 @@ Rectangle {
readonly property color _containerBg: Theme.surfaceContainerHigh readonly property color _containerBg: Theme.surfaceContainerHigh
color: { color: _containerBg
const baseColor = bodyMouse.containsMouse ? Theme.widgetBaseHoverColor : _containerBg
return baseColor
}
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10)
border.width: 0 border.width: 1
antialiasing: true antialiasing: true
readonly property color _labelPrimary: Theme.surfaceText readonly property color _labelPrimary: Theme.surfaceText
readonly property color _labelSecondary: Theme.surfaceVariantText readonly property color _labelSecondary: Theme.surfaceVariantText
readonly property color _tileBgActive: Theme.primary readonly property color _tileBgActive: Theme.primary
readonly property color _tileBgInactive: { readonly property color _tileBgInactive: {
const transparency = Theme.popupTransparency const transparency = Theme.popupTransparency || 0.92
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
return Qt.rgba(surface.r, surface.g, surface.b, transparency) return Qt.rgba(surface.r, surface.g, surface.b, transparency)
} }
@@ -49,7 +46,7 @@ Rectangle {
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22) Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
readonly property color _tileRingInactive: readonly property color _tileRingInactive:
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18) Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
readonly property color _tileIconActive: Theme.primaryText readonly property color _tileIconActive: Theme.primaryContainer
readonly property color _tileIconInactive: Theme.primary readonly property color _tileIconInactive: Theme.primary
property int _padH: Theme.spacingS property int _padH: Theme.spacingS

View File

@@ -20,7 +20,11 @@ Row {
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
@@ -30,9 +34,7 @@ Row {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (defaultSource) { if (defaultSource) {
AudioService.suppressOSD = true
defaultSource.audio.muted = !defaultSource.audio.muted defaultSource.audio.muted = !defaultSource.audio.muted
AudioService.suppressOSD = false
} }
} }
} }
@@ -67,9 +69,6 @@ Row {
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
onIsDraggingChanged: {
AudioService.suppressOSD = isDragging
}
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (defaultSource) { if (defaultSource) {
defaultSource.audio.volume = newValue / 100.0 defaultSource.audio.volume = newValue / 100.0

View File

@@ -28,14 +28,10 @@ Rectangle {
readonly property color _tileBgInactive: Theme.surfaceContainerHigh readonly property color _tileBgInactive: Theme.surfaceContainerHigh
readonly property color _tileRingActive: readonly property color _tileRingActive:
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22) Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
readonly property color _tileIconActive: Theme.primaryText readonly property color _tileIconActive: Theme.primaryContainer
readonly property color _tileIconInactive: Theme.primary readonly property color _tileIconInactive: Theme.primary
color: { color: isActive ? _tileBgActive : _tileBgInactive
if (isActive) return _tileBgActive
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
return baseColor
}
border.color: isActive ? _tileRingActive : "transparent" border.color: isActive ? _tileRingActive : "transparent"
border.width: isActive ? 1 : 0 border.width: isActive ? 1 : 0
antialiasing: true antialiasing: true
@@ -91,6 +87,13 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -12,7 +12,6 @@ Rectangle {
property real iconRotation: 0 property real iconRotation: 0
signal clicked() signal clicked()
signal iconRotationCompleted()
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48 width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
height: 48 height: 48
@@ -30,14 +29,10 @@ Rectangle {
readonly property color _tileBgInactive: Theme.surfaceContainerHigh readonly property color _tileBgInactive: Theme.surfaceContainerHigh
readonly property color _tileRingActive: readonly property color _tileRingActive:
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22) Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
readonly property color _tileIconActive: Theme.primaryText readonly property color _tileIconActive: Theme.primaryContainer
readonly property color _tileIconInactive: Theme.primary readonly property color _tileIconInactive: Theme.primary
color: { color: isActive ? _tileBgActive : _tileBgInactive
if (isActive) return _tileBgActive
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
return baseColor
}
border.color: isActive ? _tileRingActive : "transparent" border.color: isActive ? _tileRingActive : "transparent"
border.width: isActive ? 1 : 0 border.width: isActive ? 1 : 0
antialiasing: true antialiasing: true
@@ -59,7 +54,6 @@ Rectangle {
size: Theme.iconSize size: Theme.iconSize
color: isActive ? _tileIconActive : _tileIconInactive color: isActive ? _tileIconActive : _tileIconInactive
rotation: iconRotation rotation: iconRotation
onRotationCompleted: root.iconRotationCompleted()
} }
MouseArea { MouseArea {
@@ -71,6 +65,13 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -12,9 +12,8 @@ Rectangle {
property bool enabled: true property bool enabled: true
property string secondaryText: "" property string secondaryText: ""
property real iconRotation: 0 property real iconRotation: 0
signal clicked() signal clicked()
signal iconRotationCompleted()
width: parent ? parent.width : 200 width: parent ? parent.width : 200
height: 60 height: 60
@@ -28,13 +27,9 @@ Rectangle {
readonly property color _tileRingActive: readonly property color _tileRingActive:
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22) Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
color: { color: isActive ? _tileBgActive : _tileBgInactive
if (isActive) return _tileBgActive
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
return baseColor
}
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 0 border.width: isActive ? 1 : 1
opacity: enabled ? 1.0 : 0.6 opacity: enabled ? 1.0 : 0.6
function hoverTint(base) { function hoverTint(base) {
@@ -47,7 +42,7 @@ Rectangle {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0) color: mouseArea.containsMouse ? hoverTint(_containerBg) : "transparent"
opacity: mouseArea.containsMouse ? 0.08 : 0.0 opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity { Behavior on opacity {
@@ -64,10 +59,9 @@ Rectangle {
DankIcon { DankIcon {
name: root.iconName name: root.iconName
size: Theme.iconSize size: Theme.iconSize
color: isActive ? Theme.primaryText : Theme.primary color: isActive ? Theme.primaryContainer : Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
rotation: root.iconRotation rotation: root.iconRotation
onRotationCompleted: root.iconRotationCompleted()
} }
Item { Item {
@@ -84,7 +78,7 @@ Rectangle {
width: parent.width width: parent.width
text: root.text text: root.text
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: isActive ? Theme.primaryText : Theme.surfaceText color: isActive ? Theme.primaryContainer : Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
@@ -94,7 +88,7 @@ Rectangle {
width: parent.width width: parent.width
text: root.secondaryText text: root.secondaryText
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: isActive ? Theme.primaryText : Theme.surfaceVariantText color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
visible: text.length > 0 visible: text.length > 0
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
@@ -112,6 +106,13 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -1,179 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Services
Item {
id: root
required property var barWindow
required property var axis
required property var appDrawerLoader
required property var dankDashPopoutLoader
required property var processListPopoutLoader
required property var notificationCenterLoader
required property var batteryPopoutLoader
required property var vpnPopoutLoader
required property var controlCenterLoader
required property var clipboardHistoryModalPopup
required property var systemUpdateLoader
required property var notepadInstance
property alias reveal: core.reveal
property alias autoHide: core.autoHide
property alias backgroundTransparency: core.backgroundTransparency
property alias hasActivePopout: core.hasActivePopout
property alias mouseArea: topBarMouseArea
Item {
id: inputMask
readonly property int barThickness: barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing)
readonly property bool showing: SettingsData.dankBarVisible && (core.reveal
|| (CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview)
|| !core.autoHide)
readonly property int maskThickness: showing ? barThickness : 1
x: {
if (!axis.isVertical) {
return 0
} else {
switch (SettingsData.dankBarPosition) {
case SettingsData.Position.Left: return 0
case SettingsData.Position.Right: return parent.width - maskThickness
default: return 0
}
}
}
y: {
if (axis.isVertical) {
return 0
} else {
switch (SettingsData.dankBarPosition) {
case SettingsData.Position.Top: return 0
case SettingsData.Position.Bottom: return parent.height - maskThickness
default: return 0
}
}
}
width: axis.isVertical ? maskThickness : parent.width
height: axis.isVertical ? parent.height : maskThickness
}
Region {
id: mask
item: inputMask
}
property alias maskRegion: mask
QtObject {
id: core
property real backgroundTransparency: SettingsData.dankBarTransparency
property bool autoHide: SettingsData.dankBarAutoHide
property bool revealSticky: false
property bool notepadInstanceVisible: notepadInstance?.isVisible ?? false
readonly property bool hasActivePopout: {
const loaders = [{
"loader": appDrawerLoader,
"prop": "shouldBeVisible"
}, {
"loader": dankDashPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": processListPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": notificationCenterLoader,
"prop": "shouldBeVisible"
}, {
"loader": batteryPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": vpnPopoutLoader,
"prop": "shouldBeVisible"
}, {
"loader": controlCenterLoader,
"prop": "shouldBeVisible"
}, {
"loader": clipboardHistoryModalPopup,
"prop": "visible"
}, {
"loader": systemUpdateLoader,
"prop": "shouldBeVisible"
}]
return notepadInstanceVisible || loaders.some(item => {
if (item.loader) {
return item.loader?.item?.[item.prop]
}
return false
})
}
property bool reveal: {
if (CompositorService.isNiri && NiriService.inOverview) {
return SettingsData.dankBarOpenOnOverview
}
return SettingsData.dankBarVisible && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky)
}
onHasActivePopoutChanged: {
if (!hasActivePopout && autoHide && !topBarMouseArea.containsMouse) {
revealSticky = true
revealHold.restart()
}
}
}
Timer {
id: revealHold
interval: 250
repeat: false
onTriggered: core.revealSticky = false
}
Connections {
function onDankBarTransparencyChanged() {
core.backgroundTransparency = SettingsData.dankBarTransparency
}
target: SettingsData
}
Connections {
target: topBarMouseArea
function onContainsMouseChanged() {
if (topBarMouseArea.containsMouse) {
core.revealSticky = true
revealHold.stop()
} else {
if (core.autoHide && !core.hasActivePopout) {
revealHold.restart()
}
}
}
}
MouseArea {
id: topBarMouseArea
y: !barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? parent.height - height : 0) : 0
x: barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.width - width : 0) : 0
height: !barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
width: barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
anchors {
left: !barWindow.isVertical ? parent.left : (SettingsData.dankBarPosition === SettingsData.Position.Left ? parent.left : undefined)
right: !barWindow.isVertical ? parent.right : (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.right : undefined)
top: barWindow.isVertical ? parent.top : undefined
bottom: barWindow.isVertical ? parent.bottom : undefined
}
hoverEnabled: SettingsData.dankBarAutoHide && !core.reveal
acceptedButtons: Qt.NoButton
enabled: SettingsData.dankBarAutoHide && !core.reveal
}
}

View File

@@ -1,57 +0,0 @@
import QtQuick
QtObject {
id: root
property string edge: "top"
readonly property string orientation: isVertical ? "vertical" : "horizontal"
readonly property bool isVertical: edge === "left" || edge === "right"
readonly property bool isHorizontal: !isVertical
function primarySize(item) {
return isVertical ? item.height : item.width
}
function crossSize(item) {
return isVertical ? item.width : item.height
}
function setPrimaryPos(item, value) {
if (isVertical) {
item.y = value
} else {
item.x = value
}
}
function getPrimaryPos(item) {
return isVertical ? item.y : item.x
}
function primaryAnchor(anchors) {
return isVertical ? anchors.verticalCenter : anchors.horizontalCenter
}
function crossAnchor(anchors) {
return isVertical ? anchors.horizontalCenter : anchors.verticalCenter
}
function outerVisualEdge() {
if (edge === "bottom") return "bottom"
if (edge === "left") return "right"
if (edge === "right") return "left"
if (edge === "top") return "top"
return "bottom"
}
signal axisEdgeChanged()
signal axisOrientationChanged()
signal changed() // Single coalesced signal
onEdgeChanged: {
axisEdgeChanged()
axisOrientationChanged()
changed()
}
}

View File

@@ -1,210 +0,0 @@
import QtQuick
import qs.Common
Item {
id: root
required property var barWindow
required property var axis
anchors.fill: parent
anchors.left: parent.left
anchors.top: parent.top
anchors.leftMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "right" ? barWindow._wingR : 0)
anchors.rightMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "left" ? barWindow._wingR : 0)
anchors.topMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "bottom" ? barWindow._wingR : 0)
anchors.bottomMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0)
Canvas {
id: barShape
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.FramebufferObject
renderStrategy: Canvas.Cooperative
readonly property real correctWidth: root.width
readonly property real correctHeight: root.height
canvasSize: Qt.size(barWindow.px(correctWidth), barWindow.px(correctHeight))
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
onWingChanged: requestPaint()
onRtChanged: requestPaint()
onCorrectWidthChanged: requestPaint()
onCorrectHeightChanged: requestPaint()
onVisibleChanged: if (visible) requestPaint()
Component.onCompleted: requestPaint()
Connections {
target: barWindow
function on_BgColorChanged() { barShape.requestPaint() }
function on_DprChanged() { barShape.requestPaint() }
}
Connections {
target: Theme
function onIsLightModeChanged() { barShape.requestPaint() }
}
onPaint: {
const ctx = getContext("2d")
const scale = barWindow._dpr
const W = barWindow.px(barWindow.isVertical ? correctHeight : correctWidth)
const H_raw = barWindow.px(barWindow.isVertical ? correctWidth : correctHeight)
const R = barWindow.px(wing)
const RT = barWindow.px(rt)
const H = H_raw - (R > 0 ? R : 0)
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
ctx.scale(scale, scale)
function drawTopPath() {
ctx.beginPath()
ctx.moveTo(RT, 0)
ctx.lineTo(W - RT, 0)
ctx.arcTo(W, 0, W, RT, RT)
ctx.lineTo(W, H)
if (R > 0) {
ctx.lineTo(W, H + R)
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
ctx.lineTo(R, H)
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
ctx.lineTo(0, H + R)
} else {
ctx.lineTo(W, H - RT)
ctx.arcTo(W, H, W - RT, H, RT)
ctx.lineTo(RT, H)
ctx.arcTo(0, H, 0, H - RT, RT)
}
ctx.lineTo(0, RT)
ctx.arcTo(0, 0, RT, 0, RT)
ctx.closePath()
}
ctx.reset()
ctx.clearRect(0, 0, W, H_raw)
ctx.save()
if (isBottom) {
ctx.translate(W, H_raw)
ctx.rotate(Math.PI)
} else if (isLeft) {
ctx.translate(0, W)
ctx.rotate(-Math.PI / 2)
} else if (isRight) {
ctx.translate(H_raw, 0)
ctx.rotate(Math.PI / 2)
}
drawTopPath()
ctx.restore()
ctx.fillStyle = barWindow._bgColor
ctx.fill()
}
}
Canvas {
id: barTint
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.FramebufferObject
renderStrategy: Canvas.Cooperative
readonly property real correctWidth: root.width
readonly property real correctHeight: root.height
canvasSize: Qt.size(barWindow.px(correctWidth), barWindow.px(correctHeight))
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
onWingChanged: requestPaint()
onRtChanged: requestPaint()
onAlphaTintChanged: requestPaint()
onCorrectWidthChanged: requestPaint()
onCorrectHeightChanged: requestPaint()
onVisibleChanged: if (visible) requestPaint()
Component.onCompleted: requestPaint()
Connections {
target: barWindow
function on_BgColorChanged() { barTint.requestPaint() }
function on_DprChanged() { barTint.requestPaint() }
}
Connections {
target: Theme
function onIsLightModeChanged() { barTint.requestPaint() }
}
onPaint: {
const ctx = getContext("2d")
const scale = barWindow._dpr
const W = barWindow.px(barWindow.isVertical ? correctHeight : correctWidth)
const H_raw = barWindow.px(barWindow.isVertical ? correctWidth : correctHeight)
const R = barWindow.px(wing)
const RT = barWindow.px(rt)
const H = H_raw - (R > 0 ? R : 0)
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
ctx.scale(scale, scale)
function drawTopPath() {
ctx.beginPath()
ctx.moveTo(RT, 0)
ctx.lineTo(W - RT, 0)
ctx.arcTo(W, 0, W, RT, RT)
ctx.lineTo(W, H)
if (R > 0) {
ctx.lineTo(W, H + R)
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
ctx.lineTo(R, H)
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
ctx.lineTo(0, H + R)
} else {
ctx.lineTo(W, H - RT)
ctx.arcTo(W, H, W - RT, H, RT)
ctx.lineTo(RT, H)
ctx.arcTo(0, H, 0, H - RT, RT)
}
ctx.lineTo(0, RT)
ctx.arcTo(0, 0, RT, 0, RT)
ctx.closePath()
}
ctx.reset()
ctx.clearRect(0, 0, W, H_raw)
ctx.save()
if (isBottom) {
ctx.translate(W, H_raw)
ctx.rotate(Math.PI)
} else if (isLeft) {
ctx.translate(0, W)
ctx.rotate(-Math.PI / 2)
} else if (isRight) {
ctx.translate(H_raw, 0)
ctx.rotate(Math.PI / 2)
}
drawTopPath()
ctx.restore()
ctx.fillStyle = Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, alphaTint)
ctx.fill()
}
}
}

View File

@@ -1,444 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
Item {
id: root
property var widgetsModel: null
property var components: null
property bool noBackground: false
required property var axis
property string section: "center"
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property bool isVertical: axis?.isVertical ?? false
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
property var centerWidgets: []
property int totalWidgets: 0
property real totalSize: 0
function updateLayout() {
const containerSize = isVertical ? height : width
if (containerSize <= 0 || !visible) {
return
}
centerWidgets = []
totalWidgets = 0
totalSize = 0
let configuredWidgets = 0
for (var i = 0; i < centerRepeater.count; i++) {
const item = centerRepeater.itemAt(i)
if (item && getWidgetVisible(item.widgetId)) {
configuredWidgets++
if (item.active && item.item) {
centerWidgets.push(item.item)
totalWidgets++
totalSize += isVertical ? item.item.height : item.item.width
}
}
}
if (totalWidgets > 1) {
totalSize += spacing * (totalWidgets - 1)
}
positionWidgets(configuredWidgets)
}
function positionWidgets(configuredWidgets) {
if (totalWidgets === 0 || (isVertical ? height : width) <= 0) {
return
}
const parentCenter = (isVertical ? height : width) / 2
const isOdd = configuredWidgets % 2 === 1
centerWidgets.forEach(widget => {
if (isVertical) {
widget.anchors.verticalCenter = undefined
} else {
widget.anchors.horizontalCenter = undefined
}
})
if (isOdd) {
const middleIndex = Math.floor(configuredWidgets / 2)
let currentActiveIndex = 0
let middleWidget = null
for (var i = 0; i < centerRepeater.count; i++) {
const item = centerRepeater.itemAt(i)
if (item && getWidgetVisible(item.widgetId)) {
if (currentActiveIndex === middleIndex && item.active && item.item) {
middleWidget = item.item
break
}
currentActiveIndex++
}
}
if (middleWidget) {
const middleSize = isVertical ? middleWidget.height : middleWidget.width
if (isVertical) {
middleWidget.y = parentCenter - (middleSize / 2)
} else {
middleWidget.x = parentCenter - (middleSize / 2)
}
let leftWidgets = []
let rightWidgets = []
let foundMiddle = false
for (var i = 0; i < centerWidgets.length; i++) {
if (centerWidgets[i] === middleWidget) {
foundMiddle = true
continue
}
if (!foundMiddle) {
leftWidgets.push(centerWidgets[i])
} else {
rightWidgets.push(centerWidgets[i])
}
}
let currentPos = isVertical ? middleWidget.y : middleWidget.x
for (var i = leftWidgets.length - 1; i >= 0; i--) {
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
currentPos -= (spacing + size)
if (isVertical) {
leftWidgets[i].y = currentPos
} else {
leftWidgets[i].x = currentPos
}
}
currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize
for (var i = 0; i < rightWidgets.length; i++) {
currentPos += spacing
if (isVertical) {
rightWidgets[i].y = currentPos
} else {
rightWidgets[i].x = currentPos
}
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
}
}
} else {
let configuredLeftIndex = (configuredWidgets / 2) - 1
let configuredRightIndex = configuredWidgets / 2
const halfSpacing = spacing / 2
let leftWidget = null
let rightWidget = null
let leftWidgets = []
let rightWidgets = []
let currentConfigIndex = 0
for (var i = 0; i < centerRepeater.count; i++) {
const item = centerRepeater.itemAt(i)
if (item && getWidgetVisible(item.widgetId)) {
if (item.active && item.item) {
if (currentConfigIndex < configuredLeftIndex) {
leftWidgets.push(item.item)
} else if (currentConfigIndex === configuredLeftIndex) {
leftWidget = item.item
} else if (currentConfigIndex === configuredRightIndex) {
rightWidget = item.item
} else {
rightWidgets.push(item.item)
}
}
currentConfigIndex++
}
}
if (leftWidget && rightWidget) {
const leftSize = isVertical ? leftWidget.height : leftWidget.width
if (isVertical) {
leftWidget.y = parentCenter - halfSpacing - leftSize
rightWidget.y = parentCenter + halfSpacing
} else {
leftWidget.x = parentCenter - halfSpacing - leftSize
rightWidget.x = parentCenter + halfSpacing
}
let currentPos = isVertical ? leftWidget.y : leftWidget.x
for (var i = leftWidgets.length - 1; i >= 0; i--) {
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
currentPos -= (spacing + size)
if (isVertical) {
leftWidgets[i].y = currentPos
} else {
leftWidgets[i].x = currentPos
}
}
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
for (var i = 0; i < rightWidgets.length; i++) {
currentPos += spacing
if (isVertical) {
rightWidgets[i].y = currentPos
} else {
rightWidgets[i].x = currentPos
}
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
}
} else if (leftWidget && !rightWidget) {
const leftSize = isVertical ? leftWidget.height : leftWidget.width
if (isVertical) {
leftWidget.y = parentCenter - halfSpacing - leftSize
} else {
leftWidget.x = parentCenter - halfSpacing - leftSize
}
let currentPos = isVertical ? leftWidget.y : leftWidget.x
for (var i = leftWidgets.length - 1; i >= 0; i--) {
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
currentPos -= (spacing + size)
if (isVertical) {
leftWidgets[i].y = currentPos
} else {
leftWidgets[i].x = currentPos
}
}
currentPos = (isVertical ? leftWidget.y + leftWidget.height : leftWidget.x + leftWidget.width) + spacing
for (var i = 0; i < rightWidgets.length; i++) {
currentPos += spacing
if (isVertical) {
rightWidgets[i].y = currentPos
} else {
rightWidgets[i].x = currentPos
}
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
}
} else if (!leftWidget && rightWidget) {
if (isVertical) {
rightWidget.y = parentCenter + halfSpacing
} else {
rightWidget.x = parentCenter + halfSpacing
}
let currentPos = (isVertical ? rightWidget.y : rightWidget.x) - spacing
for (var i = leftWidgets.length - 1; i >= 0; i--) {
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
currentPos -= size
if (isVertical) {
leftWidgets[i].y = currentPos
} else {
leftWidgets[i].x = currentPos
}
currentPos -= spacing
}
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
for (var i = 0; i < rightWidgets.length; i++) {
currentPos += spacing
if (isVertical) {
rightWidgets[i].y = currentPos
} else {
rightWidgets[i].x = currentPos
}
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
}
} else if (totalWidgets === 1 && centerWidgets[0]) {
const size = isVertical ? centerWidgets[0].height : centerWidgets[0].width
if (isVertical) {
centerWidgets[0].y = parentCenter - (size / 2)
} else {
centerWidgets[0].x = parentCenter - (size / 2)
}
}
}
}
function getWidgetVisible(widgetId) {
const widgetVisibility = {
"cpuUsage": DgopService.dgopAvailable,
"memUsage": DgopService.dgopAvailable,
"cpuTemp": DgopService.dgopAvailable,
"gpuTemp": DgopService.dgopAvailable,
"network_speed_monitor": DgopService.dgopAvailable
}
return widgetVisibility[widgetId] ?? true
}
function getWidgetComponent(widgetId) {
// Build dynamic component map including plugins
let baseMap = {
"launcherButton": "launcherButtonComponent",
"workspaceSwitcher": "workspaceSwitcherComponent",
"focusedWindow": "focusedWindowComponent",
"runningApps": "runningAppsComponent",
"clock": "clockComponent",
"music": "mediaComponent",
"weather": "weatherComponent",
"systemTray": "systemTrayComponent",
"privacyIndicator": "privacyIndicatorComponent",
"clipboard": "clipboardComponent",
"cpuUsage": "cpuUsageComponent",
"memUsage": "memUsageComponent",
"diskUsage": "diskUsageComponent",
"cpuTemp": "cpuTempComponent",
"gpuTemp": "gpuTempComponent",
"notificationButton": "notificationButtonComponent",
"battery": "batteryComponent",
"controlCenterButton": "controlCenterButtonComponent",
"idleInhibitor": "idleInhibitorComponent",
"spacer": "spacerComponent",
"separator": "separatorComponent",
"network_speed_monitor": "networkComponent",
"keyboard_layout_name": "keyboardLayoutNameComponent",
"vpn": "vpnComponent",
"notepadButton": "notepadButtonComponent",
"colorPicker": "colorPickerComponent",
"systemUpdate": "systemUpdateComponent"
}
// For built-in components, get from components property
const componentKey = baseMap[widgetId]
if (componentKey && root.components[componentKey]) {
return root.components[componentKey]
}
// For plugin components, get from PluginService
let pluginMap = PluginService.getWidgetComponents()
return pluginMap[widgetId] || null
}
height: parent.height
width: parent.width
anchors.centerIn: parent
Timer {
id: layoutTimer
interval: 0
repeat: false
onTriggered: root.updateLayout()
}
Component.onCompleted: {
layoutTimer.restart()
}
onWidthChanged: {
if (width > 0) {
layoutTimer.restart()
}
}
onHeightChanged: {
if (height > 0) {
layoutTimer.restart()
}
}
onVisibleChanged: {
if (visible && (isVertical ? height : width) > 0) {
layoutTimer.restart()
}
}
Repeater {
id: centerRepeater
model: root.widgetsModel
Loader {
property string widgetId: model.widgetId
property var widgetData: model
property int spacerSize: model.size || 20
anchors.verticalCenter: !root.isVertical ? parent.verticalCenter : undefined
anchors.horizontalCenter: root.isVertical ? parent.horizontalCenter : undefined
active: root.getWidgetVisible(model.widgetId) && (model.widgetId !== "music" || MprisController.activePlayer !== null)
sourceComponent: root.getWidgetComponent(model.widgetId)
opacity: (model.enabled !== false) ? 1 : 0
asynchronous: false
onLoaded: {
if (!item) {
return
}
item.widthChanged.connect(() => layoutTimer.restart())
item.heightChanged.connect(() => layoutTimer.restart())
if (model.widgetId === "spacer") {
item.spacerSize = Qt.binding(() => model.size || 20)
}
if (root.axis && "axis" in item) {
item.axis = Qt.binding(() => root.axis)
}
if (root.axis && "isVertical" in item) {
try {
item.isVertical = Qt.binding(() => root.axis.isVertical)
} catch (e) {
}
}
// Inject properties for plugin widgets
if ("section" in item) {
item.section = root.section
}
if ("parentScreen" in item) {
item.parentScreen = Qt.binding(() => root.parentScreen)
}
if ("widgetThickness" in item) {
item.widgetThickness = Qt.binding(() => root.widgetThickness)
}
if ("barThickness" in item) {
item.barThickness = Qt.binding(() => root.barThickness)
}
// Inject PluginService for plugin widgets
if (item.pluginService !== undefined) {
if (item.pluginId !== undefined) {
item.pluginId = model.widgetId
}
item.pluginService = PluginService
}
layoutTimer.restart()
}
onActiveChanged: {
layoutTimer.restart()
}
}
}
Connections {
target: widgetsModel
function onCountChanged() {
layoutTimer.restart()
}
}
// Listen for plugin changes and refresh components
Connections {
target: PluginService
function onPluginLoaded(pluginId) {
// Force refresh of component lookups
for (var i = 0; i < centerRepeater.count; i++) {
var item = centerRepeater.itemAt(i)
if (item && item.widgetId === pluginId) {
item.sourceComponent = root.getWidgetComponent(pluginId)
}
}
}
function onPluginUnloaded(pluginId) {
// Force refresh of component lookups
for (var i = 0; i < centerRepeater.count; i++) {
var item = centerRepeater.itemAt(i)
if (item && item.widgetId === pluginId) {
item.sourceComponent = root.getWidgetComponent(pluginId)
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,82 +0,0 @@
import QtQuick
import qs.Common
Item {
id: root
property var widgetsModel: null
property var components: null
property bool noBackground: false
required property var axis
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property bool isVertical: axis?.isVertical ?? false
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
Loader {
id: layoutLoader
anchors.fill: parent
sourceComponent: root.isVertical ? columnComp : rowComp
}
Component {
id: rowComp
Row {
spacing: noBackground ? 2 : Theme.spacingXS
Repeater {
model: root.widgetsModel
Item {
width: widgetLoader.item ? widgetLoader.item.width : 0
height: widgetLoader.item ? widgetLoader.item.height : 0
WidgetHost {
id: widgetLoader
anchors.verticalCenter: parent.verticalCenter
widgetId: model.widgetId
widgetData: model
spacerSize: model.size || 20
components: root.components
isInColumn: false
axis: root.axis
section: "left"
parentScreen: root.parentScreen
widgetThickness: root.widgetThickness
barThickness: root.barThickness
}
}
}
}
}
Component {
id: columnComp
Column {
width: Math.max(parent.width, 200)
spacing: noBackground ? 2 : Theme.spacingXS
Repeater {
model: root.widgetsModel
Item {
width: parent.width
height: widgetLoader.item ? widgetLoader.item.height : 0
WidgetHost {
id: widgetLoader
anchors.horizontalCenter: parent.horizontalCenter
widgetId: model.widgetId
widgetData: model
spacerSize: model.size || 20
components: root.components
isInColumn: true
axis: root.axis
section: "left"
parentScreen: root.parentScreen
widgetThickness: root.widgetThickness
barThickness: root.barThickness
}
}
}
}
}
}

View File

@@ -1,84 +0,0 @@
import QtQuick
import qs.Common
Item {
id: root
property var widgetsModel: null
property var components: null
property bool noBackground: false
required property var axis
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property bool isVertical: axis?.isVertical ?? false
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
Loader {
id: layoutLoader
width: parent.width
height: parent.height
sourceComponent: root.isVertical ? columnComp : rowComp
}
Component {
id: rowComp
Row {
spacing: noBackground ? 2 : Theme.spacingXS
anchors.right: parent ? parent.right : undefined
Repeater {
model: root.widgetsModel
Item {
width: widgetLoader.item ? widgetLoader.item.width : 0
height: widgetLoader.item ? widgetLoader.item.height : 0
WidgetHost {
id: widgetLoader
anchors.verticalCenter: parent.verticalCenter
widgetId: model.widgetId
widgetData: model
spacerSize: model.size || 20
components: root.components
isInColumn: false
axis: root.axis
section: "right"
parentScreen: root.parentScreen
widgetThickness: root.widgetThickness
barThickness: root.barThickness
}
}
}
}
}
Component {
id: columnComp
Column {
width: parent ? parent.width : 0
spacing: noBackground ? 2 : Theme.spacingXS
Repeater {
model: root.widgetsModel
Item {
width: parent.width
height: widgetLoader.item ? widgetLoader.item.height : 0
WidgetHost {
id: widgetLoader
anchors.horizontalCenter: parent.horizontalCenter
widgetId: model.widgetId
widgetData: model
spacerSize: model.size || 20
components: root.components
isInColumn: true
axis: root.axis
section: "right"
parentScreen: root.parentScreen
widgetThickness: root.widgetThickness
barThickness: root.barThickness
}
}
}
}
}
}

View File

@@ -1,144 +0,0 @@
import QtQuick
import Quickshell.Services.Mpris
import qs.Services
Loader {
id: root
property string widgetId: ""
property var widgetData: null
property int spacerSize: 20
property var components: null
property bool isInColumn: false
property var axis: null
property string section: "center"
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
asynchronous: false
active: getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
(widgetId !== "music" || MprisController.activePlayer !== null)
sourceComponent: getWidgetComponent(widgetId, components)
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
signal contentItemReady(var item)
Binding {
target: root.item
when: root.item && "parentScreen" in root.item
property: "parentScreen"
value: root.parentScreen
restoreMode: Binding.RestoreNone
}
Binding {
target: root.item
when: root.item && "section" in root.item
property: "section"
value: root.section
restoreMode: Binding.RestoreNone
}
Binding {
target: root.item
when: root.item && "widgetThickness" in root.item
property: "widgetThickness"
value: root.widgetThickness
restoreMode: Binding.RestoreNone
}
Binding {
target: root.item
when: root.item && "barThickness" in root.item
property: "barThickness"
value: root.barThickness
restoreMode: Binding.RestoreNone
}
Binding {
target: root.item
when: root.item && "axis" in root.item
property: "axis"
value: root.axis
restoreMode: Binding.RestoreNone
}
onLoaded: {
if (item) {
contentItemReady(item)
if (widgetId === "spacer") {
item.spacerSize = Qt.binding(() => spacerSize)
}
if (axis && "isVertical" in item) {
try {
item.isVertical = axis.isVertical
} catch (e) {
}
}
if (item.pluginService !== undefined) {
if (item.pluginId !== undefined) {
item.pluginId = widgetId
}
item.pluginService = PluginService
}
}
}
function getWidgetComponent(widgetId, components) {
const componentMap = {
"launcherButton": components.launcherButtonComponent,
"workspaceSwitcher": components.workspaceSwitcherComponent,
"focusedWindow": components.focusedWindowComponent,
"runningApps": components.runningAppsComponent,
"clock": components.clockComponent,
"music": components.mediaComponent,
"weather": components.weatherComponent,
"systemTray": components.systemTrayComponent,
"privacyIndicator": components.privacyIndicatorComponent,
"clipboard": components.clipboardComponent,
"cpuUsage": components.cpuUsageComponent,
"memUsage": components.memUsageComponent,
"diskUsage": components.diskUsageComponent,
"cpuTemp": components.cpuTempComponent,
"gpuTemp": components.gpuTempComponent,
"notificationButton": components.notificationButtonComponent,
"battery": components.batteryComponent,
"controlCenterButton": components.controlCenterButtonComponent,
"idleInhibitor": components.idleInhibitorComponent,
"spacer": components.spacerComponent,
"separator": components.separatorComponent,
"network_speed_monitor": components.networkComponent,
"keyboard_layout_name": components.keyboardLayoutNameComponent,
"vpn": components.vpnComponent,
"notepadButton": components.notepadButtonComponent,
"colorPicker": components.colorPickerComponent,
"systemUpdate": components.systemUpdateComponent
}
if (componentMap[widgetId]) {
return componentMap[widgetId]
}
let pluginMap = PluginService.getWidgetComponents()
return pluginMap[widgetId] || null
}
function getWidgetVisible(widgetId, dgopAvailable) {
const widgetVisibility = {
"cpuUsage": dgopAvailable,
"memUsage": dgopAvailable,
"cpuTemp": dgopAvailable,
"gpuTemp": dgopAvailable,
"network_speed_monitor": dgopAvailable
}
return widgetVisibility[widgetId] ?? true
}
function getWidgetEnabled(enabled) {
return enabled !== false
}
}

View File

@@ -1,126 +0,0 @@
import QtQuick
import Quickshell.Services.UPower
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: battery
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool batteryPopupVisible: false
property string section: "right"
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal toggleBatteryPopup()
width: isVertical ? widgetThickness : (batteryContent.implicitWidth + horizontalPadding * 2)
height: isVertical ? (batteryColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = batteryArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
visible: true
Column {
id: batteryColumn
visible: battery.isVertical
anchors.centerIn: parent
spacing: 1
DankIcon {
name: BatteryService.getBatteryIcon()
size: Theme.iconSize - 8
color: {
if (!BatteryService.batteryAvailable) {
return Theme.surfaceText
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error
}
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
return Theme.primary
}
return Theme.surfaceText
}
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: BatteryService.batteryLevel.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
visible: BatteryService.batteryAvailable
}
}
Row {
id: batteryContent
visible: !battery.isVertical
anchors.centerIn: parent
spacing: SettingsData.dankBarNoBackground ? 1 : 2
DankIcon {
name: BatteryService.getBatteryIcon()
size: Theme.iconSize - 6
color: {
if (!BatteryService.batteryAvailable) {
return Theme.surfaceText;
}
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
return Theme.error;
}
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
return Theme.primary;
}
return Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: `${BatteryService.batteryLevel}%`
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: BatteryService.batteryAvailable
}
}
MouseArea {
id: batteryArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
toggleBatteryPopup();
}
}
}

View File

@@ -1,57 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Item {
id: root
property bool isActive: false
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right"
property var clipboardHistoryModal: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal clicked()
width: widgetThickness
height: widgetThickness
MouseArea {
id: clipboardArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
root.clicked()
}
}
Rectangle {
id: clipboardContent
anchors.fill: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent"
}
const baseColor = clipboardArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
}
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
name: "content_paste"
size: Theme.iconSize - 6
color: Theme.surfaceText
}
}
}

View File

@@ -1,247 +0,0 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool compactMode: false
property string section: "center"
property var popupTarget: null
property var parentScreen: null
property real barThickness: 48
property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
signal clockClicked
width: isVertical ? widgetThickness : (clockRow.implicitWidth + horizontalPadding * 2)
height: isVertical ? (clockColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = clockMouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
Column {
id: clockColumn
visible: root.isVertical
anchors.centerIn: parent
spacing: -2
Row {
spacing: 0
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: {
if (SettingsData.use24HourClock) {
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0)
} else {
const hours = systemClock?.date?.getHours()
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
return String(display).padStart(2, '0').charAt(0)
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
width: 9
horizontalAlignment: Text.AlignHCenter
}
StyledText {
text: {
if (SettingsData.use24HourClock) {
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1)
} else {
const hours = systemClock?.date?.getHours()
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
return String(display).padStart(2, '0').charAt(1)
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
width: 9
horizontalAlignment: Text.AlignHCenter
}
}
Row {
spacing: 0
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
width: 9
horizontalAlignment: Text.AlignHCenter
}
StyledText {
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
width: 9
horizontalAlignment: Text.AlignHCenter
}
}
Item {
width: 12
height: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
width: 12
height: 1
color: Theme.outlineButton
anchors.centerIn: parent
}
}
Row {
spacing: 0
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: {
const locale = Qt.locale()
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
return value.charAt(0)
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Light
width: 9
horizontalAlignment: Text.AlignHCenter
}
StyledText {
text: {
const locale = Qt.locale()
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
return value.charAt(1)
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Light
width: 9
horizontalAlignment: Text.AlignHCenter
}
}
Row {
spacing: 0
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: {
const locale = Qt.locale()
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
return value.charAt(0)
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Light
width: 9
horizontalAlignment: Text.AlignHCenter
}
StyledText {
text: {
const locale = Qt.locale()
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
return value.charAt(1)
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Font.Light
width: 9
horizontalAlignment: Text.AlignHCenter
}
}
}
Row {
id: clockRow
visible: !root.isVertical
anchors.centerIn: parent
spacing: Theme.spacingS
StyledText {
text: {
const format = SettingsData.use24HourClock ? "HH:mm" : "h:mm AP"
return systemClock?.date?.toLocaleTimeString(Qt.locale(), format)
}
font.pixelSize: Theme.fontSizeMedium - 1
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "•"
font.pixelSize: Theme.fontSizeSmall
color: Theme.outlineButton
anchors.verticalCenter: parent.verticalCenter
visible: !SettingsData.clockCompactMode
}
StyledText {
text: {
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
}
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
}
font.pixelSize: Theme.fontSizeMedium - 1
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: !SettingsData.clockCompactMode
}
}
SystemClock {
id: systemClock
precision: SystemClock.Seconds
}
MouseArea {
id: clockMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.clockClicked()
}
}
}

View File

@@ -1,96 +0,0 @@
import QtQuick
import Quickshell.Hyprland
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right"
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal clicked()
readonly property string focusedScreenName: (
CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") :
CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : ""
)
function resolveNotepadInstance() {
if (typeof notepadSlideoutVariants === "undefined" || !notepadSlideoutVariants || !notepadSlideoutVariants.instances) {
return null
}
const targetScreen = focusedScreenName
if (targetScreen) {
for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) {
var slideout = notepadSlideoutVariants.instances[i]
if (slideout.modelData && slideout.modelData.name === targetScreen) {
return slideout
}
}
}
return notepadSlideoutVariants.instances.length > 0 ? notepadSlideoutVariants.instances[0] : null
}
readonly property var notepadInstance: resolveNotepadInstance()
readonly property bool isActive: notepadInstance?.isVisible ?? false
width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2)
height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = notepadArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: notepadIcon
anchors.centerIn: parent
name: "assignment"
size: Theme.iconSize - 6
color: notepadArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
}
Rectangle {
width: 6
height: 6
radius: 3
color: Theme.primary
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
opacity: 0.8
}
MouseArea {
id: notepadArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
const inst = root.notepadInstance
if (inst) {
inst.toggle()
}
root.clicked()
}
}
}

View File

@@ -1,76 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Item {
id: root
property bool hasUnread: false
property bool isActive: false
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right"
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal clicked()
width: widgetThickness
height: widgetThickness
MouseArea {
id: notificationArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.clicked()
}
}
Rectangle {
id: notificationContent
anchors.fill: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent"
}
const baseColor = notificationArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
}
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
size: Theme.iconSize - 6
color: SessionData.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText)
}
Rectangle {
width: 8
height: 8
radius: 4
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
visible: root.hasUnread
}
}
}

View File

@@ -1,630 +0,0 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Services.SystemTray
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var parentWindow: null
property var parentScreen: null
property real widgetThickness: 30
property bool isAtBottom: false
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property int calculatedSize: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0
width: isVertical ? widgetThickness : calculatedSize
height: isVertical ? calculatedSize : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SystemTray.items.values.length === 0) {
return "transparent";
}
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
visible: SystemTray.items.values.length > 0
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVertical ? columnComp : rowComp
}
Component {
id: rowComp
Row {
spacing: 0
Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0];
const path = split[1];
const fileName = name.substring(name.lastIndexOf("/") + 1);
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
return `file://${icon}`;
}
return icon;
}
return "";
}
width: 24
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
}
IconImage {
anchors.centerIn: parent
width: 16
height: 16
source: parent.iconSource
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
trayItem.activate();
return ;
}
if (trayItem.hasMenu) {
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}
}
}
}
Component {
id: columnComp
Column {
spacing: 0
Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0];
const path = split[1];
const fileName = name.substring(name.lastIndexOf("/") + 1);
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
return `file://${icon}`;
}
return icon;
}
return "";
}
width: 24
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
}
IconImage {
anchors.centerIn: parent
width: 16
height: 16
source: parent.iconSource
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
trayItem.activate();
return ;
}
if (trayItem.hasMenu) {
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}
}
}
}
Component {
id: trayMenuComponent
Rectangle {
id: menuRoot
property var trayItem: null
property var anchorItem: null
property var parentScreen: null
property bool isAtBottom: false
property bool isVertical: false
property var axis: null
property bool showMenu: false
property var menuHandle: null
ListModel { id: entryStack }
function topEntry() {
return entryStack.count ? entryStack.get(entryStack.count - 1).handle : null
}
function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
trayItem = item
anchorItem = anchor
parentScreen = screen
isAtBottom = atBottom
isVertical = vertical
axis = axisObj
menuHandle = item?.menu
if (parentScreen) {
for (var i = 0; i < Quickshell.screens.length; i++) {
const s = Quickshell.screens[i]
if (s === parentScreen) {
menuWindow.screen = s
break
}
}
}
showMenu = true
}
function close() {
showMenu = false
}
function showSubMenu(entry) {
if (!entry || !entry.hasChildren) return;
entryStack.append({ handle: entry });
const h = entry.menu || entry;
if (h && typeof h.updateLayout === "function") h.updateLayout();
submenuHydrator.menu = h;
submenuHydrator.open();
Qt.callLater(() => submenuHydrator.close());
}
function goBack() {
if (!entryStack.count) return;
entryStack.remove(entryStack.count - 1);
}
width: 0
height: 0
color: "transparent"
PanelWindow {
id: menuWindow
visible: menuRoot.showMenu && (menuRoot.trayItem?.hasMenu ?? false)
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
property point anchorPos: Qt.point(screen.width / 2, screen.height / 2)
onVisibleChanged: {
if (visible) {
updatePosition()
}
}
function updatePosition() {
if (!menuRoot.anchorItem || !menuRoot.trayItem) {
anchorPos = Qt.point(screen.width / 2, screen.height / 2)
return
}
const globalPos = menuRoot.anchorItem.mapToGlobal(0, 0)
const screenX = screen.x || 0
const screenY = screen.y || 0
const relativeX = globalPos.x - screenX
const relativeY = globalPos.y - screenY
const widgetThickness = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
const effectiveBarThickness = Math.max(widgetThickness + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
if (menuRoot.isVertical) {
const edge = menuRoot.axis?.edge
let targetX
if (edge === "left") {
targetX = effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance
} else {
const popupX = effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance
targetX = screen.width - popupX
}
anchorPos = Qt.point(targetX, relativeY + menuRoot.anchorItem.height / 2)
} else {
let targetY
if (menuRoot.isAtBottom) {
const popupY = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance
targetY = screen.height - popupY
} else {
targetY = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance
}
anchorPos = Qt.point(relativeX + menuRoot.anchorItem.width / 2, targetY)
}
}
Rectangle {
id: menuContainer
width: Math.min(500, Math.max(250, menuColumn.implicitWidth + Theme.spacingS * 2))
height: Math.max(40, menuColumn.implicitHeight + Theme.spacingS * 2)
x: {
if (menuRoot.isVertical) {
const edge = menuRoot.axis?.edge
if (edge === "left") {
const targetX = menuWindow.anchorPos.x
return Math.min(menuWindow.screen.width - width - 10, targetX)
} else {
const targetX = menuWindow.anchorPos.x - width
return Math.max(10, targetX)
}
} else {
const left = 10
const right = menuWindow.width - width - 10
const want = menuWindow.anchorPos.x - width / 2
return Math.max(left, Math.min(right, want))
}
}
y: {
if (menuRoot.isVertical) {
const top = 10
const bottom = menuWindow.height - height - 10
const want = menuWindow.anchorPos.y - height / 2
return Math.max(top, Math.min(bottom, want))
} else {
if (menuRoot.isAtBottom) {
const targetY = menuWindow.anchorPos.y - height
return Math.max(10, targetY)
} else {
const targetY = menuWindow.anchorPos.y
return Math.min(menuWindow.screen.height - height - 10, targetY)
}
}
}
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
opacity: menuRoot.showMenu ? 1 : 0
scale: menuRoot.showMenu ? 1 : 0.85
Rectangle {
anchors.fill: parent
anchors.topMargin: 4
anchors.leftMargin: 2
anchors.rightMargin: -2
anchors.bottomMargin: -4
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
}
QsMenuAnchor {
id: submenuHydrator
anchor.window: menuWindow
}
QsMenuOpener {
id: rootOpener
menu: menuRoot.menuHandle
}
QsMenuOpener {
id: subOpener
menu: {
const e = menuRoot.topEntry();
return e ? (e.menu || e) : null;
}
}
Column {
id: menuColumn
width: parent.width - Theme.spacingS * 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.spacingS
spacing: 1
Rectangle {
visible: entryStack.count > 0
width: parent.width
height: 28
radius: Theme.cornerRadius
color: backArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankIcon {
name: "arrow_back"
size: 16
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Back"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: backArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: menuRoot.goBack()
}
}
Rectangle {
visible: entryStack.count > 0
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
Repeater {
model: entryStack.count
? (subOpener.children ? subOpener.children
: (menuRoot.topEntry()?.children || []))
: rootOpener.children
Rectangle {
property var menuEntry: modelData
width: menuColumn.width
height: menuEntry?.isSeparator ? 1 : 28
radius: menuEntry?.isSeparator ? 0 : Theme.cornerRadius
color: {
if (menuEntry?.isSeparator) {
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
return itemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
}
MouseArea {
id: itemArea
anchors.fill: parent
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!menuEntry || menuEntry.isSeparator) return;
if (menuEntry.hasChildren) {
menuRoot.showSubMenu(menuEntry);
} else {
if (typeof menuEntry.activate === "function") {
menuEntry.activate();
} else if (typeof menuEntry.triggered === "function") {
menuEntry.triggered();
}
Qt.createQmlObject('import QtQuick; Timer { interval: 80; running: true; repeat: false; onTriggered: menuRoot.close() }', menuRoot);
}
}
}
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
visible: !menuEntry?.isSeparator
Rectangle {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: menuEntry?.buttonType !== undefined && menuEntry.buttonType !== 0
radius: menuEntry?.buttonType === 2 ? 8 : 2
border.width: 1
border.color: Theme.outline
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width - 6
height: parent.height - 6
radius: parent.radius - 3
color: Theme.primary
visible: menuEntry?.checkState === 2
}
DankIcon {
anchors.centerIn: parent
name: "check"
size: 10
color: Theme.primaryText
visible: menuEntry?.buttonType === 1 && menuEntry?.checkState === 2
}
}
Item {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: menuEntry?.icon && menuEntry.icon !== ""
Image {
anchors.fill: parent
source: menuEntry?.icon || ""
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
smooth: true
}
}
StyledText {
text: menuEntry?.text || ""
font.pixelSize: Theme.fontSizeSmall
color: (menuEntry?.enabled !== false) ? Theme.surfaceText : Theme.surfaceTextMedium
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
width: Math.max(150, parent.width - 64)
wrapMode: Text.NoWrap
}
Item {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
DankIcon {
anchors.centerIn: parent
name: "chevron_right"
size: 14
color: Theme.surfaceText
visible: menuEntry?.hasChildren ?? false
}
}
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
z: -1
onClicked: menuRoot.close()
}
}
}
}
property var currentTrayMenu: null
function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
if (currentTrayMenu) {
currentTrayMenu.destroy()
}
currentTrayMenu = trayMenuComponent.createObject(null)
if (currentTrayMenu) {
currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom, vertical ?? false, axisObj)
}
}
}

View File

@@ -1,154 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false
property string section: "right"
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking
signal clicked()
width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2)
height: isVertical ? widgetThickness : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: statusIcon
anchors.centerIn: parent
visible: root.isVertical
name: {
if (isChecking) return "refresh";
if (SystemUpdateService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (SystemUpdateService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimation
target: statusIcon
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIcon.rotation = 0
}
}
}
}
Rectangle {
width: 8
height: 8
radius: 4
color: Theme.error
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
visible: root.isVertical && root.hasUpdates && !root.isChecking
}
Row {
id: updaterIcon
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: !root.isVertical
DankIcon {
id: statusIconHorizontal
anchors.verticalCenter: parent.verticalCenter
name: {
if (isChecking) return "refresh";
if (SystemUpdateService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (SystemUpdateService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimationHorizontal
target: statusIconHorizontal
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIconHorizontal.rotation = 0
}
}
}
}
StyledText {
id: countText
anchors.verticalCenter: parent.verticalCenter
text: SystemUpdateService.updateCount.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
visible: hasUpdates && !isChecking
}
}
MouseArea {
id: updaterArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.clicked();
}
}
}

View File

@@ -1,103 +0,0 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property int widgetThickness: 28
property int barThickness: 32
property string section: "right"
property var popupTarget: null
property var parentScreen: null
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal toggleVpnPopup()
width: isVertical ? widgetThickness : (Theme.iconSize + horizontalPadding * 2)
height: isVertical ? (Theme.iconSize + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = clickArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: icon
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
size: Theme.iconSize - 6
color: VpnService.connected ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
Loader {
id: tooltipLoader
active: false
sourceComponent: DankTooltip {}
}
MouseArea {
id: clickArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.toggleVpnPopup();
}
onEntered: {
if (root.parentScreen && !(popupTarget && popupTarget.shouldBeVisible)) {
tooltipLoader.active = true
if (tooltipLoader.item) {
let tooltipText = ""
if (!VpnService.connected) {
tooltipText = "VPN Disconnected"
} else {
const names = VpnService.activeNames || []
if (names.length <= 1) {
tooltipText = "VPN Connected • " + (names[0] || "")
} else {
tooltipText = "VPN Connected • " + names[0] + " +" + (names.length - 1)
}
}
if (root.isVertical) {
const globalPos = mapToGlobal(width / 2, height / 2)
const screenX = root.parentScreen ? root.parentScreen.x : 0
const screenY = root.parentScreen ? root.parentScreen.y : 0
const relativeY = globalPos.y - screenY
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS) : (root.parentScreen.width - Theme.barHeight - SettingsData.dankBarSpacing - Theme.spacingXS)
const isLeft = root.axis?.edge === "left"
tooltipLoader.item.show(tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft)
} else {
const globalPos = mapToGlobal(width / 2, height)
const tooltipY = Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS
tooltipLoader.item.show(tooltipText, globalPos.x, tooltipY, root.parentScreen, false, false)
}
}
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
}
tooltipLoader.active = false
}
}
}

View File

@@ -13,33 +13,30 @@ DankPopout {
id: root id: root
property bool dashVisible: false property bool dashVisible: false
property string triggerSection: "center"
property var triggerScreen: null property var triggerScreen: null
property int currentTabIndex: 0 property int currentTabIndex: 0
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
triggerSection = section if (section === "center") {
triggerScreen = screen
triggerY = y
if (section === "center" && (SettingsData.dankBarPosition === SettingsData.Position.Top || SettingsData.dankBarPosition === SettingsData.Position.Bottom)) {
const screenWidth = screen ? screen.width : Screen.width const screenWidth = screen ? screen.width : Screen.width
triggerX = (screenWidth - popupWidth) / 2 triggerX = (screenWidth - popupWidth) / 2
triggerWidth = popupWidth triggerWidth = popupWidth
} else if (section === "center" && (SettingsData.dankBarPosition === SettingsData.Position.Left || SettingsData.dankBarPosition === SettingsData.Position.Right)) {
const screenHeight = screen ? screen.height : Screen.height
triggerX = (screenHeight - popupHeight) / 2
triggerWidth = popupHeight
} else { } else {
triggerX = x triggerX = x
triggerWidth = width triggerWidth = width
} }
triggerY = y
triggerSection = section
triggerScreen = screen
} }
popupWidth: 700 popupWidth: 700
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500
triggerX: Screen.width - 620 - Theme.spacingL triggerX: Screen.width - 620 - Theme.spacingL
triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2 triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
triggerWidth: 80 triggerWidth: 80
positioning: "center"
shouldBeVisible: dashVisible shouldBeVisible: dashVisible
visible: shouldBeVisible visible: shouldBeVisible

File diff suppressed because it is too large Load Diff

View File

@@ -13,22 +13,83 @@ Card {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM spacing: Theme.spacingM
DankCircularImage { Item {
id: avatarContainer id: avatarContainer
property bool hasImage: profileImageLoader.status === Image.Ready
width: 77 width: 77
height: 77 height: 77
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
imageSource: {
if (PortalService.profileImage === "") Rectangle {
return "" anchors.fill: parent
radius: 36
if (PortalService.profileImage.startsWith("/")) color: Theme.primary
return "file://" + PortalService.profileImage visible: !avatarContainer.hasImage
return PortalService.profileImage StyledText {
anchors.centerIn: parent
text: UserInfoService.username.length > 0 ? UserInfoService.username.charAt(0).toUpperCase() : "b"
font.pixelSize: Theme.fontSizeXLarge + 4
font.weight: Font.Bold
color: Theme.background
}
}
Image {
id: profileImageLoader
source: {
if (PortalService.profileImage === "")
return ""
if (PortalService.profileImage.startsWith("/"))
return "file://" + PortalService.profileImage
return PortalService.profileImage
}
smooth: true
asynchronous: true
mipmap: true
cache: true
visible: false
}
MultiEffect {
anchors.fill: parent
anchors.margins: 2
source: profileImageLoader
maskEnabled: true
maskSource: circularMask
visible: avatarContainer.hasImage
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
Item {
id: circularMask
width: 77 - 4
height: 77 - 4
layer.enabled: true
layer.smooth: true
visible: false
Rectangle {
anchors.fill: parent
radius: width / 2
color: "black"
antialiasing: true
}
}
DankIcon {
anchors.centerIn: parent
name: "person"
size: Theme.iconSize + 8
color: Theme.error
visible: PortalService.profileImage !== "" && profileImageLoader.status === Image.Error
} }
fallbackIcon: "person"
} }
Column { Column {
@@ -43,7 +104,7 @@ Card {
elide: Text.ElideRight elide: Text.ElideRight
width: parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 width: parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3
} }
Row { Row {
spacing: Theme.spacingS spacing: Theme.spacingS
@@ -67,7 +128,7 @@ Card {
width: parent.parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 - 16 - Theme.spacingS width: parent.parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 - 16 - Theme.spacingS
} }
} }
Row { Row {
spacing: Theme.spacingS spacing: Theme.spacingS
@@ -80,7 +141,7 @@ Card {
StyledText { StyledText {
id: uptimeText id: uptimeText
property real availableWidth: parent.parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 - 16 - Theme.spacingS property real availableWidth: parent.parent.parent.parent.width - avatarContainer.width - Theme.spacingM * 3 - 16 - Theme.spacingS
property real longTextWidth: { property real longTextWidth: {
const fontSize = Math.round(Theme.fontSizeSmall || 12) const fontSize = Math.round(Theme.fontSizeSmall || 12)

View File

@@ -7,59 +7,22 @@ import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
pragma ComponentBehavior: Bound PanelWindow {
id: dock
Variants { WlrLayershell.namespace: "quickshell:dock"
id: dockVariants
model: SettingsData.getFilteredScreens("dock")
WlrLayershell.layer: WlrLayershell.Top
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
property var modelData
property var contextMenu property var contextMenu
delegate: PanelWindow {
id: dock
WlrLayershell.namespace: "quickshell:dock"
readonly property bool isVertical: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
anchors {
top: !isVertical ? (SettingsData.dockPosition === SettingsData.Position.Top) : true
bottom: !isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom) : true
left: !isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Left)
right: !isVertical ? true : (SettingsData.dockPosition === SettingsData.Position.Right)
}
property var modelData: item
property bool autoHide: SettingsData.dockAutoHide property bool autoHide: SettingsData.dockAutoHide
property real backgroundTransparency: SettingsData.dockTransparency property real backgroundTransparency: SettingsData.dockTransparency
property bool groupByApp: SettingsData.dockGroupByApp property bool groupByApp: SettingsData.dockGroupByApp
readonly property real widgetHeight: Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6) property bool contextMenuOpen: (contextMenu && contextMenu.visible && contextMenu.screen === modelData)
readonly property real effectiveBarHeight: Math.max(widgetHeight + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
readonly property real barSpacing: {
const barIsHorizontal = (SettingsData.dankBarPosition === SettingsData.Position.Top || SettingsData.dankBarPosition === SettingsData.Position.Bottom)
const barIsVertical = (SettingsData.dankBarPosition === SettingsData.Position.Left || SettingsData.dankBarPosition === SettingsData.Position.Right)
const samePosition = (SettingsData.dockPosition === SettingsData.dankBarPosition)
const dockIsHorizontal = !isVertical
const dockIsVertical = isVertical
if (!SettingsData.dankBarVisible) return 0
if (dockIsHorizontal && barIsHorizontal && samePosition) {
return SettingsData.dankBarSpacing + effectiveBarHeight + SettingsData.dankBarBottomGap
}
if (dockIsVertical && barIsVertical && samePosition) {
return SettingsData.dankBarSpacing + effectiveBarHeight + SettingsData.dankBarBottomGap
}
return 0
}
readonly property real dockMargin: SettingsData.dockSpacing
readonly property real positionSpacing: barSpacing + SettingsData.dockBottomGap
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
function px(v) { return Math.round(v * _dpr) / _dpr }
property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData)
property bool windowIsFullscreen: { property bool windowIsFullscreen: {
if (!ToplevelManager.activeToplevel) { if (!ToplevelManager.activeToplevel) {
return false return false
@@ -68,28 +31,7 @@ Variants {
const fullscreenApps = ["vlc", "mpv", "kodi", "steam", "lutris", "wine", "dosbox"] const fullscreenApps = ["vlc", "mpv", "kodi", "steam", "lutris", "wine", "dosbox"]
return fullscreenApps.some(app => activeWindow.appId && activeWindow.appId.toLowerCase().includes(app)) return fullscreenApps.some(app => activeWindow.appId && activeWindow.appId.toLowerCase().includes(app))
} }
property bool revealSticky: false property bool reveal: (!autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen) && !windowIsFullscreen
Timer {
id: revealHold
interval: 250
repeat: false
onTriggered: dock.revealSticky = false
}
property bool reveal: {
if (CompositorService.isNiri && NiriService.inOverview) {
return SettingsData.dockOpenOnOverview
}
return (!autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky) && !windowIsFullscreen
}
onContextMenuOpenChanged: {
if (!contextMenuOpen && autoHide && !dockMouseArea.containsMouse) {
revealSticky = true
revealHold.restart()
}
}
Connections { Connections {
target: SettingsData target: SettingsData
@@ -102,253 +44,152 @@ Variants {
visible: SettingsData.showDock visible: SettingsData.showDock
color: "transparent" color: "transparent"
anchors {
exclusiveZone: { bottom: true
if (!SettingsData.showDock || autoHide) return -1 left: true
if (barSpacing > 0) return -1 right: true
return px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap)
} }
margins {
left: 0
right: 0
}
implicitHeight: 100
exclusiveZone: autoHide ? -1 : 65 - 16
mask: Region { mask: Region {
item: dockMouseArea item: dockMouseArea
} }
Rectangle { MouseArea {
id: appTooltip id: dockMouseArea
z: 1000 property real currentScreen: modelData ? modelData : dock.screen
property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920
property real maxDockWidth: Math.min(screenWidth * 0.8, 1200)
property var hoveredButton: { height: dock.reveal ? 65 : 20
if (!dockApps.children[0]) { width: dock.reveal ? Math.min(dockBackground.width + 32, maxDockWidth) : Math.min(Math.max(dockBackground.width + 64, 200), screenWidth * 0.5)
return null anchors {
} bottom: parent.bottom
const layoutItem = dockApps.children[0] horizontalCenter: parent.horizontalCenter
const flowLayout = layoutItem.children[0]
let repeater = null
for (var i = 0; i < flowLayout.children.length; i++) {
const child = flowLayout.children[i]
if (child && typeof child.count !== "undefined" && typeof child.itemAt === "function") {
repeater = child
break
}
}
if (!repeater || !repeater.itemAt) {
return null
}
for (var i = 0; i < repeater.count; i++) {
const item = repeater.itemAt(i)
if (item && item.dockButton && item.dockButton.showTooltip) {
return item.dockButton
}
}
return null
} }
hoverEnabled: true
property string tooltipText: hoveredButton ? hoveredButton.tooltipText : "" Behavior on height {
NumberAnimation {
visible: hoveredButton !== null && tooltipText !== "" duration: 200
width: px(tooltipLabel.implicitWidth + 24) easing.type: Easing.OutCubic
height: px(tooltipLabel.implicitHeight + 12)
color: Theme.surfaceContainer
radius: Theme.cornerRadius
border.width: 1
border.color: Theme.outlineMedium
x: {
if (!hoveredButton) return 0
const buttonPos = hoveredButton.mapToItem(dock.contentItem, 0, 0)
if (!dock.isVertical) {
return buttonPos.x + hoveredButton.width / 2 - width / 2
} else {
if (SettingsData.dockPosition === SettingsData.Position.Right) {
return buttonPos.x - width - Theme.spacingS
} else {
return buttonPos.x + hoveredButton.width + Theme.spacingS
}
}
}
y: {
if (!hoveredButton) return 0
const buttonPos = hoveredButton.mapToItem(dock.contentItem, 0, 0)
if (!dock.isVertical) {
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
return buttonPos.y - height - Theme.spacingS
} else {
return buttonPos.y + hoveredButton.height + Theme.spacingS
}
} else {
return buttonPos.y + hoveredButton.height / 2 - height / 2
} }
} }
StyledText { Item {
id: tooltipLabel id: dockContainer
anchors.centerIn: parent anchors.fill: parent
text: appTooltip.tooltipText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
}
}
Item { transform: Translate {
id: dockCore id: dockSlide
anchors.fill: parent y: dock.reveal ? 0 : 60
Connections { Behavior on y {
target: dockMouseArea NumberAnimation {
function onContainsMouseChanged() { duration: 200
if (dockMouseArea.containsMouse) { easing.type: Easing.OutCubic
dock.revealSticky = true
revealHold.stop()
} else {
if (dock.autoHide && !dock.contextMenuOpen) {
revealHold.restart()
} }
} }
} }
}
MouseArea { Rectangle {
id: dockMouseArea id: dockBackground
property real currentScreen: modelData ? modelData : dock.screen objectName: "dockBackground"
property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920 anchors {
property real screenHeight: currentScreen ? currentScreen.geometry.height : 1080 top: parent.top
property real maxDockWidth: Math.min(screenWidth * 0.8, 1200) bottom: parent.bottom
property real maxDockHeight: Math.min(screenHeight * 0.8, 1200) horizontalCenter: parent.horizontalCenter
height: {
if (dock.isVertical) {
return dock.reveal ? Math.min(dockBackground.implicitHeight + 32, maxDockHeight) : Math.min(Math.max(dockBackground.implicitHeight + 64, 200), screenHeight * 0.5)
} else {
return dock.reveal ? px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap) : 1
} }
}
width: {
if (dock.isVertical) {
return dock.reveal ? px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap) : 1
} else {
return dock.reveal ? Math.min(dockBackground.implicitWidth + 32, maxDockWidth) : Math.min(Math.max(dockBackground.implicitWidth + 64, 200), screenWidth * 0.5)
}
}
anchors {
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.left) : undefined
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
}
hoverEnabled: true
acceptedButtons: Qt.NoButton
Behavior on height { width: dockApps.implicitWidth + 12
NumberAnimation { height: parent.height - 8
duration: 200
easing.type: Easing.OutCubic
}
}
Behavior on width { anchors.topMargin: 4
NumberAnimation { anchors.bottomMargin: 1
duration: 200
easing.type: Easing.OutCubic
}
}
Item { color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency)
id: dockContainer radius: Theme.cornerRadius
anchors.fill: parent border.width: 1
border.color: Theme.outlineMedium
transform: Translate { layer.enabled: true
id: dockSlide
x: {
if (!dock.isVertical) return 0
if (dock.reveal) return 0
if (SettingsData.dockPosition === SettingsData.Position.Right) {
return 60
} else {
return -60
}
}
y: {
if (dock.isVertical) return 0
if (dock.reveal) return 0
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
return 60
} else {
return -60
}
}
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
Behavior on y {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
Rectangle { Rectangle {
id: dockBackground anchors.fill: parent
objectName: "dockBackground" color: Qt.rgba(Theme.surfaceTint.r, Theme.surfaceTint.g, Theme.surfaceTint.b, 0.04)
anchors { radius: parent.radius
top: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top) : undefined }
bottom: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined) : undefined
horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined
left: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.left) : undefined
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
}
anchors.topMargin: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? 0 : barSpacing + 4) : 0
anchors.bottomMargin: !dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Bottom ? barSpacing + 1 : 0) : 0
anchors.leftMargin: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? 0 : barSpacing + 4) : 0
anchors.rightMargin: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? barSpacing + 1 : 0) : 0
implicitWidth: dock.isVertical ? (dockApps.implicitHeight + SettingsData.dockSpacing * 2) : (dockApps.implicitWidth + SettingsData.dockSpacing * 2) DockApps {
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2) id: dockApps
width: implicitWidth
height: implicitHeight
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency) anchors.top: parent.top
radius: Theme.cornerRadius anchors.bottom: parent.bottom
border.width: 1 anchors.horizontalCenter: parent.horizontalCenter
border.color: Theme.outlineMedium anchors.topMargin: 4
layer.enabled: true anchors.bottomMargin: 4
Rectangle { contextMenu: dock.contextMenu
anchors.fill: parent groupByApp: dock.groupByApp
color: Qt.rgba(Theme.surfaceTint.r, Theme.surfaceTint.g, Theme.surfaceTint.b, 0.04) }
radius: parent.radius }
}
Rectangle {
DockApps { id: appTooltip
id: dockApps
property var hoveredButton: {
anchors.top: !dock.isVertical ? parent.top : undefined if (!dockApps.children[0]) {
anchors.bottom: !dock.isVertical ? parent.bottom : undefined return null
anchors.horizontalCenter: !dock.isVertical ? parent.horizontalCenter : undefined }
anchors.left: dock.isVertical ? parent.left : undefined const row = dockApps.children[0]
anchors.right: dock.isVertical ? parent.right : undefined let repeater = null
anchors.verticalCenter: dock.isVertical ? parent.verticalCenter : undefined for (var i = 0; i < row.children.length; i++) {
anchors.topMargin: !dock.isVertical ? SettingsData.dockSpacing : 0 const child = row.children[i]
anchors.bottomMargin: !dock.isVertical ? SettingsData.dockSpacing : 0 if (child && typeof child.count !== "undefined" && typeof child.itemAt === "function") {
anchors.leftMargin: dock.isVertical ? SettingsData.dockSpacing : 0 repeater = child
anchors.rightMargin: dock.isVertical ? SettingsData.dockSpacing : 0 break
}
contextMenu: dockVariants.contextMenu }
groupByApp: dock.groupByApp if (!repeater || !repeater.itemAt) {
isVertical: dock.isVertical return null
} }
for (var i = 0; i < repeater.count; i++) {
const item = repeater.itemAt(i)
if (item && item.dockButton && item.dockButton.showTooltip) {
return item.dockButton
}
}
return null
}
property string tooltipText: hoveredButton ? hoveredButton.tooltipText : ""
visible: hoveredButton !== null && tooltipText !== ""
width: tooltipLabel.implicitWidth + 24
height: tooltipLabel.implicitHeight + 12
color: Theme.surfaceContainer
radius: Theme.cornerRadius
border.width: 1
border.color: Theme.outlineMedium
y: -height - 8
x: hoveredButton ? hoveredButton.mapToItem(dockContainer, hoveredButton.width / 2, 0).x - width / 2 : 0
StyledText {
id: tooltipLabel
anchors.centerIn: parent
text: appTooltip.tooltipText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
} }
} }
}
} }
} }
} }

View File

@@ -25,27 +25,6 @@ Item {
property string windowTitle: "" property string windowTitle: ""
property bool isHovered: mouseArea.containsMouse && !dragging property bool isHovered: mouseArea.containsMouse && !dragging
property bool showTooltip: mouseArea.containsMouse && !dragging property bool showTooltip: mouseArea.containsMouse && !dragging
property var cachedDesktopEntry: null
function updateDesktopEntry() {
if (!appData || appData.appId === "__SEPARATOR__") {
cachedDesktopEntry = null
return
}
const moddedId = Paths.moddedAppId(appData.appId)
cachedDesktopEntry = DesktopEntries.heuristicLookup(moddedId)
}
Component.onCompleted: updateDesktopEntry()
onAppDataChanged: updateDesktopEntry()
Connections {
target: DesktopEntries
function onApplicationsChanged() {
updateDesktopEntry()
}
}
property bool isWindowFocused: { property bool isWindowFocused: {
if (!appData) { if (!appData) {
return false return false
@@ -76,7 +55,8 @@ Item {
} }
if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) { if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) {
const appName = cachedDesktopEntry && cachedDesktopEntry.name ? cachedDesktopEntry.name : appData.appId const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
const appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
const title = appData.type === "window" ? windowTitle : appData.windowTitle const title = appData.type === "window" ? windowTitle : appData.windowTitle
return appName + (title ? " • " + title : "") return appName + (title ? " • " + title : "")
} }
@@ -85,7 +65,8 @@ Item {
return "" return ""
} }
return cachedDesktopEntry && cachedDesktopEntry.name ? cachedDesktopEntry.name : appData.appId const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
return desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
} }
width: 40 width: 40
@@ -274,7 +255,7 @@ Item {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (appData.type === "pinned") { if (appData.type === "pinned") {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = cachedDesktopEntry const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -294,7 +275,7 @@ Item {
} else if (appData.type === "grouped") { } else if (appData.type === "grouped") {
if (appData.windowCount === 0) { if (appData.windowCount === 0) {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = cachedDesktopEntry const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -324,7 +305,7 @@ Item {
} }
} else if (mouse.button === Qt.MiddleButton) { } else if (mouse.button === Qt.MiddleButton) {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = cachedDesktopEntry const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -360,7 +341,8 @@ Item {
if (moddedId.toLowerCase().includes("steam_app")) { if (moddedId.toLowerCase().includes("steam_app")) {
return "" return ""
} }
return cachedDesktopEntry && cachedDesktopEntry.icon ? Quickshell.iconPath(cachedDesktopEntry.icon, true) : "" const desktopEntry = DesktopEntries.heuristicLookup(moddedId)
return desktopEntry && desktopEntry.icon ? Quickshell.iconPath(desktopEntry.icon, true) : ""
} }
mipmap: true mipmap: true
smooth: true smooth: true
@@ -399,7 +381,7 @@ Item {
return "?" return "?"
} }
const desktopEntry = cachedDesktopEntry const desktopEntry = DesktopEntries.heuristicLookup(appData.appId)
if (desktopEntry && desktopEntry.name) { if (desktopEntry && desktopEntry.name) {
return desktopEntry.name.charAt(0).toUpperCase() return desktopEntry.name.charAt(0).toUpperCase()
} }

View File

@@ -13,10 +13,9 @@ Item {
property bool requestDockShow: false property bool requestDockShow: false
property int pinnedAppCount: 0 property int pinnedAppCount: 0
property bool groupByApp: false property bool groupByApp: false
property bool isVertical: false
implicitWidth: isVertical ? appLayout.height : appLayout.width implicitWidth: row.width
implicitHeight: isVertical ? appLayout.width : appLayout.height implicitHeight: row.height
function movePinnedApp(fromIndex, toIndex) { function movePinnedApp(fromIndex, toIndex) {
if (fromIndex === toIndex) { if (fromIndex === toIndex) {
@@ -34,16 +33,11 @@ Item {
SessionData.setPinnedApps(currentPinned) SessionData.setPinnedApps(currentPinned)
} }
Item { Row {
id: appLayout id: row
spacing: 2
anchors.centerIn: parent anchors.centerIn: parent
width: layoutFlow.width height: 40
height: layoutFlow.height
Flow {
id: layoutFlow
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
spacing: 8
Repeater { Repeater {
id: repeater id: repeater
@@ -224,7 +218,6 @@ Item {
} }
} }
} }
}
} }
Connections { Connections {

View File

@@ -92,41 +92,17 @@ PanelWindow {
} }
const dockBackground = findDockBackground(dockWindow.contentItem) const dockBackground = findDockBackground(dockWindow.contentItem)
let actualDockWidth = dockWindow.width
if (dockBackground) { if (dockBackground) {
actualDockHeight = dockBackground.height actualDockHeight = dockBackground.height
actualDockWidth = dockBackground.width
} }
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right const dockBottomMargin = 16
const dockMargin = 16 const buttonScreenY = root.screen.height - actualDockHeight - dockBottomMargin - 20
let buttonScreenX, buttonScreenY
if (isVertical) { const dockContentWidth = dockWindow.width
const dockContentHeight = dockWindow.height const screenWidth = root.screen.width
const screenHeight = root.screen.height const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2)
const dockTopMargin = Math.round((screenHeight - dockContentHeight) / 2) const buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2
buttonScreenY = dockTopMargin + buttonPosInDock.y + anchorItem.height / 2
if (SettingsData.dockPosition === SettingsData.Position.Right) {
buttonScreenX = root.screen.width - actualDockWidth - dockMargin - 20
} else {
buttonScreenX = actualDockWidth + dockMargin + 20
}
} else {
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
if (isDockAtBottom) {
buttonScreenY = root.screen.height - actualDockHeight - dockMargin - 20
} else {
buttonScreenY = actualDockHeight + dockMargin + 20
}
const dockContentWidth = dockWindow.width
const screenWidth = root.screen.width
const dockLeftMargin = Math.round((screenWidth - dockContentWidth) / 2)
buttonScreenX = dockLeftMargin + buttonPosInDock.x + anchorItem.width / 2
}
anchorPos = Qt.point(buttonScreenX, buttonScreenY) anchorPos = Qt.point(buttonScreenX, buttonScreenY)
} }
@@ -138,37 +114,12 @@ PanelWindow {
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2) height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
x: { x: {
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right const left = 10
if (isVertical) { const right = root.width - width - 10
const isDockAtRight = SettingsData.dockPosition === SettingsData.Position.Right const want = root.anchorPos.x - width / 2
if (isDockAtRight) { return Math.max(left, Math.min(right, want))
return Math.max(10, root.anchorPos.x - width + 30)
} else {
return Math.min(root.width - width - 10, root.anchorPos.x - 30)
}
} else {
const left = 10
const right = root.width - width - 10
const want = root.anchorPos.x - width / 2
return Math.max(left, Math.min(right, want))
}
}
y: {
const isVertical = SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
if (isVertical) {
const top = 10
const bottom = root.height - height - 10
const want = root.anchorPos.y - height / 2
return Math.max(top, Math.min(bottom, want))
} else {
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
if (isDockAtBottom) {
return Math.max(10, root.anchorPos.y - height + 30)
} else {
return Math.min(root.height - height - 10, root.anchorPos.y - 30)
}
}
} }
y: Math.max(10, root.anchorPos.y - height + 30)
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
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)

View File

@@ -1,105 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
Singleton {
id: root
readonly property string greetCfgDir: Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
readonly property string sessionConfigPath: greetCfgDir + "/session.json"
readonly property string memoryFile: greetCfgDir + "/memory.json"
property string lastSessionId: ""
property string lastSuccessfulUser: ""
property bool isLightMode: false
property bool nightModeEnabled: false
Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", greetCfgDir])
loadMemory()
loadSessionConfig()
}
function loadMemory() {
parseMemory(memoryFileView.text())
}
function loadSessionConfig() {
parseSessionConfig(sessionConfigFileView.text())
}
function parseSessionConfig(content) {
try {
if (content && content.trim()) {
const config = JSON.parse(content)
isLightMode = config.isLightMode !== undefined ? config.isLightMode : false
nightModeEnabled = config.nightModeEnabled !== undefined ? config.nightModeEnabled : false
}
} catch (e) {
console.warn("Failed to parse greeter session config:", e)
}
}
function parseMemory(content) {
try {
if (content && content.trim()) {
const memory = JSON.parse(content)
lastSessionId = memory.lastSessionId !== undefined ? memory.lastSessionId : ""
lastSuccessfulUser = memory.lastSuccessfulUser !== undefined ? memory.lastSuccessfulUser : ""
}
} catch (e) {
console.warn("Failed to parse greetd memory:", e)
}
}
function saveMemory() {
memoryFileView.setText(JSON.stringify({
"lastSessionId": lastSessionId,
"lastSuccessfulUser": lastSuccessfulUser
}, null, 2))
}
function setLastSessionId(id) {
lastSessionId = id || ""
saveMemory()
}
function setLastSuccessfulUser(username) {
lastSuccessfulUser = username || ""
saveMemory()
}
FileView {
id: memoryFileView
path: root.memoryFile
blockLoading: false
blockWrites: false
atomicWrites: true
watchChanges: false
printErrors: false
onLoaded: {
parseMemory(memoryFileView.text())
}
}
FileView {
id: sessionConfigFileView
path: root.sessionConfigPath
blockLoading: false
blockWrites: true
atomicWrites: false
watchChanges: false
printErrors: true
onLoaded: {
parseSessionConfig(sessionConfigFileView.text())
}
onLoadFailed: error => {
console.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error)
}
}
}

View File

@@ -1,114 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
Singleton {
id: root
readonly property string configPath: {
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/etc/greetd/.dms"
return greetCfgDir + "/settings.json"
}
property string currentThemeName: "blue"
property bool settingsLoaded: false
property string customThemeFile: ""
property string matugenScheme: "scheme-tonal-spot"
property bool use24HourClock: true
property bool useFahrenheit: false
property bool nightModeEnabled: false
property string weatherLocation: "New York, NY"
property string weatherCoordinates: "40.7128,-74.0060"
property bool useAutoLocation: false
property bool weatherEnabled: true
property string iconTheme: "System Default"
property bool useOSLogo: false
property string osLogoColorOverride: ""
property real osLogoBrightness: 0.5
property real osLogoContrast: 1
property string fontFamily: "Inter Variable"
property string monoFontFamily: "Fira Code"
property int fontWeight: Font.Normal
property real fontScale: 1.0
property real cornerRadius: 12
property string widgetBackgroundColor: "sch"
property string surfaceBase: "s"
property string lockDateFormat: ""
property bool lockScreenShowPowerActions: true
property var screenPreferences: ({})
property int animationSpeed: 2
readonly property string defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code"
function parseSettings(content) {
try {
if (content && content.trim()) {
const settings = JSON.parse(content)
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "blue"
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
weatherLocation = settings.weatherLocation !== undefined ? settings.weatherLocation : "New York, NY"
weatherCoordinates = settings.weatherCoordinates !== undefined ? settings.weatherCoordinates : "40.7128,-74.0060"
useAutoLocation = settings.useAutoLocation !== undefined ? settings.useAutoLocation : false
weatherEnabled = settings.weatherEnabled !== undefined ? settings.weatherEnabled : true
iconTheme = settings.iconTheme !== undefined ? settings.iconTheme : "System Default"
useOSLogo = settings.useOSLogo !== undefined ? settings.useOSLogo : false
osLogoColorOverride = settings.osLogoColorOverride !== undefined ? settings.osLogoColorOverride : ""
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1
fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : defaultFontFamily
monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily
fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal
fontScale = settings.fontScale !== undefined ? settings.fontScale : 1.0
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch"
surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s"
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2
settingsLoaded = true
if (typeof Theme !== "undefined") {
Theme.applyGreeterTheme(currentThemeName)
}
}
} catch (e) {
console.warn("Failed to parse greetd settings:", e)
}
}
function getEffectiveLockDateFormat() {
return lockDateFormat && lockDateFormat.length > 0 ? lockDateFormat : Locale.LongFormat
}
function getFilteredScreens(componentId) {
const prefs = screenPreferences && screenPreferences[componentId] || ["all"]
if (prefs.includes("all")) {
return Quickshell.screens
}
return Quickshell.screens.filter(screen => prefs.includes(screen.name))
}
FileView {
id: settingsFile
path: root.configPath
blockLoading: false
blockWrites: true
atomicWrites: false
watchChanges: false
printErrors: true
onLoaded: {
parseSettings(settingsFile.text())
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
import QtQuick
import Quickshell
pragma Singleton
pragma ComponentBehavior: Bound
Singleton {
id: root
property string passwordBuffer: ""
property string username: ""
property string usernameInput: ""
property bool showPasswordInput: false
property string selectedSession: ""
property string pamState: ""
property bool unlocking: false
property var sessionList: []
property var sessionExecs: []
property var sessionPaths: []
property int currentSessionIndex: 0
function reset() {
showPasswordInput = false
username = ""
usernameInput = ""
passwordBuffer = ""
pamState = ""
}
}

View File

@@ -1,18 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Greetd
WlSessionLockSurface {
id: root
required property WlSessionLock lock
color: "transparent"
GreeterContent {
anchors.fill: parent
screenName: root.screen?.name ?? ""
sessionLock: root.lock
}
}

View File

@@ -1,100 +0,0 @@
# Dank (dms) Greeter
A greeter for [greetd](https://github.com/kennylevinsen/greetd) that follows the aesthetics of the dms lock screen.
## Features
- **Multi user**: Login with any system user
- **dms sync**: Sync settings with dms for consistent styling between shell and greeter
- **niri or Hyprland**: Use either niri or Hyprland for the greeter's compositor.
- **Custom PAM**: Supports custom PAM configuration in `/etc/pam.d/dankshell`
- **Session Memory**: Remembers last selected session and user
## Installation
### Automatic
The easiest thing is to run `dms greeter install` or `dms` for interactive installation.
### Manual
1. Install `greetd` (in most distro's standard repositories)
2. Copy `assets/dms-niri.kdl` or `assets/dms-hypr.conf` to `/etc/greetd`
- niri if you want to run the greeter under niri, hypr if you want to run the greeter under Hyprland
3. Copy `assets/greet-niri.sh` or `assets/greet-hyprland.sh` to `/usr/local/bin/start-dms-greetd.sh`
4. Edit `/etc/greetd/dms-niri.kdl` or `/etc/greetd/dms-hypr.conf` and replace `_DMS_PATH_` with the absolute path to dms, e.g. `/home/joecool/.config/quickshell/dms`
5. Edit or create `/etc/greetd/config.toml`
```toml
[terminal]
# The VT to run the greeter on. Can be "next", "current" or a number
# designating the VT.
vt = 1
# The default session, also known as the greeter.
[default_session]
# `agreety` is the bundled agetty/login-lookalike. You can replace `/bin/sh`
# with whatever you want started, such as `sway`.
# The user to run the command as. The privileges this user must have depends
# on the greeter. A graphical greeter may for example require the user to be
# in the `video` group.
user = "greeter"
command = "/usr/local/bin/start-dms-greetd.sh"
```
Enable the greeter with `sudo systemctl enable greetd`
### NixOS
To install the greeter on NixOS add the repo to your flake inputs as described in the readme. Then somewhere in your NixOS config add this to imports:
```nix
imports = [
inputs.dankMaterialShell.nixosModules.greeter
]
```
Enable the greeter with this in your NixOS config:
```nix
programs.dankMaterialShell.greeter = {
enable = true;
compositor = "niri"; # or set to hyprland
configHome = "/home/user"; # optionally symlinks that users DMS settings to the greeters data directory
};
```
## Usage
To run dms in greeter mode you just need to set `DMS_RUN_GREETER=1` in the environment.
```bash
DMS_RUN_GREETER=1 qs -p /path/to/dms
```
### Configuration
#### Compositor
You can configure compositor specific settings such as outputs/displays the same as you would in niri or Hyprland.
Simply edit `/etc/greetd/dms-niri.kdl` or `/etc/greetd/dms-hypr.conf` to change compositor settings for the greeter
#### Personalization
Wallpapers and themes and weather and clock formats and things are a TODO on the documentation, but it's configured exactly the same as dms.
You can synchronize those configurations with a specific user if you want greeter settings to always mirror the shell.
```bash
# For core settings (theme, clock formats, etc)
sudo ln -sf ~/.config/DankMaterialShell/settings.json /etc/greetd/.dms/settings.json
# For state (mainly you would configure wallpaper in this file)
sudo ln -sf ~/.local/state/DankMaterialShell/session.json /etc/greetd/.dms/session.json
# For wallpaper based theming
sudo ln -sf ~/.cache/quickshell/dankshell/dms-colors.json /etc/greetd/.dms/dms-colors.json
```
You can override the configuration path with the `DMS_GREET_CFG_DIR` environment variable, the default is `/etc/greetd/.dms`
It should be writable by the greeter user.

View File

@@ -1,3 +0,0 @@
env = DMS_RUN_GREETER,1
exec = sh -c "qs -p _DMS_PATH_; hyprctl dispatch exit"

View File

@@ -1,23 +0,0 @@
hotkey-overlay {
skip-at-startup
}
environment {
DMS_RUN_GREETER "1"
}
spawn-at-startup "sh" "-c" "qs -p _DMS_PATH_; niri msg action quit --skip-confirmation"
debug {
keep-max-bpc-unchanged
}
gestures {
hot-corners {
off
}
}
layout {
background-color "#000000"
}

View File

@@ -1,8 +0,0 @@
#!/bin/sh
export XDG_SESSION_TYPE=wayland
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export EGL_PLATFORM=gbm
exec Hyprland -c /etc/greetd/dms-hypr.conf

View File

@@ -1,8 +0,0 @@
#!/bin/sh
export XDG_SESSION_TYPE=wayland
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export EGL_PLATFORM=gbm
exec niri -c /etc/greetd/dms-niri.kdl

View File

@@ -20,9 +20,6 @@ Item {
property bool unlocking: false property bool unlocking: false
property string pamState: "" property string pamState: ""
property string randomFact: "" property string randomFact: ""
property string hyprlandCurrentLayout: ""
property string hyprlandKeyboard: ""
property int hyprlandLayoutCount: 0
signal unlockRequested signal unlockRequested
@@ -47,8 +44,10 @@ Item {
powerDialogVisible = false powerDialogVisible = false
} }
property var facts: ["A photon takes 100,000 to 200,000 years bouncing through the Sun's dense core, then races to Earth in just 8 minutes 20 seconds.", "A teaspoon of neutron star matter would weigh a billion metric tons here on Earth.", "Right now, 100 trillion solar neutrinos are passing through your body every second.", "The Sun converts 4 million metric tons of matter into pure energy every second—enough to power Earth for 500,000 years.", "The universe still glows with leftover heat from the Big Bang—just 2.7 degrees above absolute zero.", "There's a nebula out there that's actually colder than empty space itself.", "We've detected black holes crashing together by measuring spacetime stretch by less than 1/10,000th the width of a proton.", "Fast radio bursts can release more energy in 5 milliseconds than our Sun produces in 3 days.", "Our galaxy might be crawling with billions of rogue planets drifting alone in the dark.", "Distant galaxies can move away from us faster than light because space itself is stretching.", "The edge of what we can see is 46.5 billion light-years away, even though the universe is only 13.8 billion years old.", "The universe is mostly invisible: 5% regular matter, 27% dark matter, 68% dark energy.", "A day on Venus lasts longer than its entire year around the Sun.", "On Mercury, the time between sunrises is 176 Earth days long.", "In about 4.5 billion years, our galaxy will smash into Andromeda.", "Most of the gold in your jewelry was forged when neutron stars collided somewhere in space.", "PSR J1748-2446ad, the fastest spinning star, rotates 716 times per second—its equator moves at 24% the speed of light.", "Cosmic rays create particles that shouldn't make it to Earth's surface, but time dilation lets them sneak through.", "Jupiter's magnetic field is so huge that if we could see it, it would look bigger than the Moon in our sky.", "Interstellar space is so empty it's like a cube 32 kilometers wide containing just a single grain of sand.", "Voyager 1 is 24 billion kilometers away but won't leave the Sun's gravitational influence for another 30,000 years.", "Counting to a billion at one number per second would take over 31 years.", "Space is so vast, even speeding at light-speed, you'd never return past the cosmic horizon.", "Astronauts on the ISS age about 0.01 seconds less each year than people on Earth.", "Sagittarius B2, a dust cloud near our galaxy's center, contains ethyl formate—the compound that gives raspberries their flavor and rum its smell.", "Beyond 16 billion light-years, the cosmic event horizon marks where space expands too fast for light to ever reach us again.", "Even at light-speed, you'd never catch up to most galaxies—space expands faster.", "Only around 5% of galaxies are ever reachable—even at light-speed.", "If the Sun vanished, we'd still orbit it for 8 minutes before drifting away.", "If a planet 65 million light-years away looked at Earth now, it'd see dinosaurs.", "Our oldest radio signals will reach the Milky Way's center in 26,000 years.", "Every atom in your body heavier than hydrogen was forged in the nuclear furnace of a dying star.", "The Moon moves 3.8 centimeters farther from Earth every year.", "The universe creates 275 million new stars every single day.", "Jupiter's Great Red Spot is a storm twice the size of Earth that has been raging for at least 350 years.", "If you watched someone fall into a black hole, they'd appear frozen at the event horizon forever—time effectively stops from your perspective.", "The Boötes Supervoid is a cosmic desert 1.8 billion light-years across with 60% fewer galaxies than it should have."]
function pickRandomFact() { function pickRandomFact() {
randomFact = Facts.getRandomFact() randomFact = facts[Math.floor(Math.random() * facts.length)]
} }
Component.onCompleted: { Component.onCompleted: {
@@ -58,11 +57,6 @@ Item {
WeatherService.addRef() WeatherService.addRef()
UserInfoService.refreshUserInfo() UserInfoService.refreshUserInfo()
if (CompositorService.isHyprland) {
updateHyprlandLayout()
hyprlandLayoutUpdateTimer.start()
}
} }
onDemoModeChanged: { onDemoModeChanged: {
if (demoMode) { if (demoMode) {
@@ -71,56 +65,6 @@ Item {
} }
Component.onDestruction: { Component.onDestruction: {
WeatherService.removeRef() WeatherService.removeRef()
if (CompositorService.isHyprland) {
hyprlandLayoutUpdateTimer.stop()
}
}
function updateHyprlandLayout() {
if (CompositorService.isHyprland) {
hyprlandLayoutProcess.running = true
}
}
Process {
id: hyprlandLayoutProcess
running: false
command: ["hyprctl", "-j", "devices"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
hyprlandKeyboard = mainKeyboard.name
if (mainKeyboard && mainKeyboard.active_keymap) {
const parts = mainKeyboard.active_keymap.split(" ")
if (parts.length > 0) {
hyprlandCurrentLayout = parts[0].substring(0, 2).toUpperCase()
} else {
hyprlandCurrentLayout = mainKeyboard.active_keymap.substring(0, 2).toUpperCase()
}
} else {
hyprlandCurrentLayout = ""
}
if (mainKeyboard && mainKeyboard.layout_names) {
hyprlandLayoutCount = mainKeyboard.layout_names.length
} else {
hyprlandLayoutCount = 0
}
} catch (e) {
hyprlandCurrentLayout = ""
hyprlandLayoutCount = 0
}
}
}
}
Timer {
id: hyprlandLayoutUpdateTimer
interval: 1000
running: false
repeat: true
onTriggered: updateHyprlandLayout()
} }
Loader { Loader {
@@ -236,21 +180,93 @@ Item {
spacing: Theme.spacingL spacing: Theme.spacingL
Layout.fillWidth: true Layout.fillWidth: true
DankCircularImage { Item {
id: avatarContainer
property bool hasImage: profileImageLoader.status === Image.Ready
Layout.preferredWidth: 60 Layout.preferredWidth: 60
Layout.preferredHeight: 60 Layout.preferredHeight: 60
imageSource: {
if (PortalService.profileImage === "") {
return ""
}
if (PortalService.profileImage.startsWith("/")) { Rectangle {
return "file://" + PortalService.profileImage anchors.fill: parent
} radius: width / 2
color: "transparent"
return PortalService.profileImage border.color: Theme.primary
border.width: 1
visible: parent.hasImage
}
Image {
id: profileImageLoader
source: {
if (PortalService.profileImage === "") {
return ""
}
if (PortalService.profileImage.startsWith("/")) {
return "file://" + PortalService.profileImage
}
return PortalService.profileImage
}
smooth: true
asynchronous: true
mipmap: true
cache: true
visible: false
}
MultiEffect {
anchors.fill: parent
anchors.margins: 5
source: profileImageLoader
maskEnabled: true
maskSource: circularMask
visible: avatarContainer.hasImage
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
Item {
id: circularMask
width: 60 - 10
height: 60 - 10
layer.enabled: true
layer.smooth: true
visible: false
Rectangle {
anchors.fill: parent
radius: width / 2
color: "black"
antialiasing: true
}
}
Rectangle {
anchors.fill: parent
radius: width / 2
color: Theme.primary
visible: !parent.hasImage
DankIcon {
anchors.centerIn: parent
name: "person"
size: Theme.iconSize + 4
color: Theme.primaryText
}
}
DankIcon {
anchors.centerIn: parent
name: "warning"
size: Theme.iconSize + 4
color: Theme.primaryText
visible: PortalService.profileImage !== "" && profileImageLoader.status === Image.Error
} }
fallbackIcon: "person"
} }
Rectangle { Rectangle {
@@ -578,7 +594,7 @@ Item {
StyledText { StyledText {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 20 Layout.preferredHeight: root.pamState ? 20 : 0
text: { text: {
if (root.pamState === "error") { if (root.pamState === "error") {
return "Authentication error - try again" return "Authentication error - try again"
@@ -594,6 +610,7 @@ Item {
color: Theme.error color: Theme.error
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: root.pamState !== ""
opacity: root.pamState !== "" ? 1 : 0 opacity: root.pamState !== "" ? 1 : 0
Behavior on opacity { Behavior on opacity {
@@ -602,6 +619,13 @@ Item {
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on Layout.preferredHeight {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }
} }
@@ -622,92 +646,6 @@ Item {
anchors.margins: Theme.spacingXL anchors.margins: Theme.spacingXL
spacing: Theme.spacingL spacing: Theme.spacingL
Item {
width: keyboardLayoutRow.width
height: keyboardLayoutRow.height
anchors.verticalCenter: parent.verticalCenter
visible: {
if (CompositorService.isNiri) {
return NiriService.keyboardLayoutNames.length > 1
} else if (CompositorService.isHyprland) {
return hyprlandLayoutCount > 1
}
return false
}
Row {
id: keyboardLayoutRow
spacing: 4
Item {
width: Theme.iconSize
height: Theme.iconSize
DankIcon {
name: "keyboard"
size: Theme.iconSize
color: "white"
anchors.centerIn: parent
}
}
Item {
width: childrenRect.width
height: Theme.iconSize
StyledText {
text: {
if (CompositorService.isNiri) {
const layout = NiriService.getCurrentKeyboardLayoutName()
if (!layout) return ""
const parts = layout.split(" ")
if (parts.length > 0) {
return parts[0].substring(0, 2).toUpperCase()
}
return layout.substring(0, 2).toUpperCase()
} else if (CompositorService.isHyprland) {
return hyprlandCurrentLayout
}
return ""
}
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Light
color: "white"
anchors.verticalCenter: parent.verticalCenter
}
}
}
MouseArea {
id: keyboardLayoutArea
anchors.fill: parent
enabled: !demoMode
hoverEnabled: enabled
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (CompositorService.isNiri) {
NiriService.cycleKeyboardLayout()
} else if (CompositorService.isHyprland) {
Quickshell.execDetached([
"hyprctl",
"switchxkblayout",
hyprlandKeyboard,
"next"
])
updateHyprlandLayout()
}
}
}
}
Rectangle {
width: 1
height: 24
color: Qt.rgba(255, 255, 255, 0.2)
anchors.verticalCenter: parent.verticalCenter
visible: MprisController.activePlayer
}
Row { Row {
spacing: Theme.spacingS spacing: Theme.spacingS
visible: MprisController.activePlayer visible: MprisController.activePlayer
@@ -1172,8 +1110,6 @@ Item {
return return
} }
console.log("Authentication failed:", res) console.log("Authentication failed:", res)
passwordField.text = ""
root.passwordBuffer = ""
if (res === PamResult.Error) if (res === PamResult.Error)
root.pamState = "error" root.pamState = "error"
else if (res === PamResult.MaxTries) else if (res === PamResult.MaxTries)

View File

@@ -163,7 +163,7 @@ Item {
DankDropdown { DankDropdown {
id: fontDropdown id: fontDropdown
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: -Theme.spacingM anchors.leftMargin: -Theme.spacingM
width: parent.width + Theme.spacingM width: parent.width + Theme.spacingM
text: "Font Family" text: "Font Family"

View File

@@ -63,7 +63,7 @@ Column {
width: calculatedWidth width: calculatedWidth
height: 32 height: 32
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryPressed, 0) color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent"
border.width: isActive ? 0 : 1 border.width: isActive ? 0 : 1
border.color: Theme.outlineMedium border.color: Theme.outlineMedium
@@ -104,7 +104,7 @@ Column {
width: 20 width: 20
height: 20 height: 20
radius: 10 radius: 10
color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0) color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : "transparent"
visible: NotepadStorageService.tabs.length > 1 visible: NotepadStorageService.tabs.length > 1
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View File

@@ -34,6 +34,12 @@ Rectangle {
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on border.color { Behavior on border.color {
ColorAnimation { ColorAnimation {
@@ -44,12 +50,12 @@ Rectangle {
color: { color: {
if (isGroupSelected && keyboardNavigationActive) { if (isGroupSelected && keyboardNavigationActive) {
return Theme.primaryPressed return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2)
} }
if (keyboardNavigationActive && expanded && selectedNotificationIndex >= 0) { if (keyboardNavigationActive && expanded && selectedNotificationIndex >= 0) {
return Theme.primaryHoverLight return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12)
} }
return Theme.surfaceContainerHigh return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
} }
border.color: { border.color: {
if (isGroupSelected && keyboardNavigationActive) { if (isGroupSelected && keyboardNavigationActive) {
@@ -344,10 +350,16 @@ Rectangle {
return baseHeight return baseHeight
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: isSelected ? Theme.primaryPressed : Theme.surfaceContainerHigh color: isSelected ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.25) : "transparent"
border.color: isSelected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05) border.color: isSelected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
border.width: isSelected ? 1 : 1 border.width: isSelected ? 1 : 1
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on border.color { Behavior on border.color {
ColorAnimation { ColorAnimation {

View File

@@ -13,6 +13,7 @@ DankPopout {
id: root id: root
property bool notificationHistoryVisible: false property bool notificationHistoryVisible: false
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
NotificationKeyboardController { NotificationKeyboardController {
@@ -34,10 +35,10 @@ DankPopout {
popupWidth: 400 popupWidth: 400
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
triggerX: 0 triggerX: Screen.width - 400 - Theme.spacingL
triggerY: 0 triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 40 triggerWidth: 40
positioning: "" positioning: "center"
screen: triggerScreen screen: triggerScreen
shouldBeVisible: notificationHistoryVisible shouldBeVisible: notificationHistoryVisible
visible: shouldBeVisible visible: shouldBeVisible
@@ -116,7 +117,7 @@ DankPopout {
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
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 border.width: 1
focus: true focus: true
Component.onCompleted: { Component.onCompleted: {

View File

@@ -106,7 +106,9 @@ Item {
height: 28 height: 28
radius: Theme.cornerRadius radius: Theme.cornerRadius
visible: NotificationService.notifications.length > 0 visible: NotificationService.notifications.length > 0
color: clearArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh color: clearArea.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.3)
border.color: clearArea.containsMouse ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
@@ -137,6 +139,19 @@ Item {
onClicked: NotificationService.clearAllNotifications() onClicked: NotificationService.clearAllNotifications()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }
} }
} }

View File

@@ -411,78 +411,6 @@ QtObject {
selectPrevious() selectPrevious()
event.accepted = true event.accepted = true
} }
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else if (selectedFlatIndex === 0) {
keyboardNavigationActive = false
if (listView) {
listView.keyboardActive = false
}
selectionVersion++
} else {
selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else if (selectedFlatIndex === 0) {
keyboardNavigationActive = false
if (listView) {
listView.keyboardActive = false
}
selectionVersion++
} else {
selectPrevious()
}
event.accepted = true
} else if (keyboardNavigationActive) { } else if (keyboardNavigationActive) {
if (event.key === Qt.Key_Space) { if (event.key === Qt.Key_Space) {
toggleGroupExpanded() toggleGroupExpanded()
@@ -497,10 +425,7 @@ QtObject {
clearSelected() clearSelected()
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Tab) { } else if (event.key === Qt.Key_Tab) {
selectNext() selectNextWrapping()
event.accepted = true
} else if (event.key === Qt.Key_Backtab) {
selectPrevious()
event.accepted = true event.accepted = true
} else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) { } else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
const actionIndex = event.key - Qt.Key_1 const actionIndex = event.key - Qt.Key_1

View File

@@ -157,6 +157,7 @@ Rectangle {
} }
DankDropdown { DankDropdown {
width: parent.width
text: "Low Priority" text: "Low Priority"
description: "Timeout for low priority notifications" description: "Timeout for low priority notifications"
currentValue: getTimeoutText(SettingsData.notificationTimeoutLow) currentValue: getTimeoutText(SettingsData.notificationTimeoutLow)
@@ -172,6 +173,7 @@ Rectangle {
} }
DankDropdown { DankDropdown {
width: parent.width
text: "Normal Priority" text: "Normal Priority"
description: "Timeout for normal priority notifications" description: "Timeout for normal priority notifications"
currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal) currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal)
@@ -187,6 +189,7 @@ Rectangle {
} }
DankDropdown { DankDropdown {
width: parent.width
text: "Critical Priority" text: "Critical Priority"
description: "Timeout for critical priority notifications" description: "Timeout for critical priority notifications"
currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical) currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical)

View File

@@ -76,6 +76,7 @@ PanelWindow {
color: "transparent" color: "transparent"
implicitWidth: 400 implicitWidth: 400
implicitHeight: 122 implicitHeight: 122
onScreenYChanged: margins.top = Theme.barHeight - 4 + SettingsData.topBarSpacing + 4 + screenY
onHasValidDataChanged: { onHasValidDataChanged: {
if (!hasValidData && !exiting && !_isDestroying) { if (!hasValidData && !exiting && !_isDestroying) {
forceExit() forceExit()
@@ -107,94 +108,14 @@ PanelWindow {
} }
} }
property bool isTopCenter: SettingsData.notificationPopupPosition === -1 anchors {
top: true
anchors.top: isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left right: true
anchors.bottom: SettingsData.notificationPopupPosition === SettingsData.Position.Bottom || SettingsData.notificationPopupPosition === SettingsData.Position.Right }
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
margins { margins {
top: getTopMargin() top: Theme.barHeight - 4 + SettingsData.topBarSpacing + 4
bottom: getBottomMargin() right: 12
left: getLeftMargin()
right: getRightMargin()
}
function getTopMargin() {
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left
if (!isTop) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
let base = Theme.popupDistance
if (barPos === SettingsData.Position.Top) {
base = exclusiveZone
}
return base + screenY
}
function getBottomMargin() {
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isBottom = popupPos === SettingsData.Position.Bottom || popupPos === SettingsData.Position.Right
if (!isBottom) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
let base = Theme.popupDistance
if (barPos === SettingsData.Position.Bottom) {
base = exclusiveZone
}
return base + screenY
}
function getLeftMargin() {
if (isTopCenter) {
return (screen.width - implicitWidth) / 2
}
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom
if (!isLeft) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
if (barPos === SettingsData.Position.Left) {
return exclusiveZone
}
return Theme.popupDistance
}
function getRightMargin() {
if (isTopCenter) return 0
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isRight = popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Right
if (!isRight) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
if (barPos === SettingsData.Position.Right) {
return exclusiveZone
}
return Theme.popupDistance
} }
Item { Item {
@@ -202,7 +123,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
visible: win.hasValidData visible: win.hasValidData
layer.enabled: true layer.enabled: (enterX.running || exitAnim.running)
layer.smooth: true layer.smooth: true
Rectangle { Rectangle {
@@ -213,7 +134,7 @@ PanelWindow {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.popupBackground() color: Theme.popupBackground()
border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 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.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 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: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0 border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 1
clip: true clip: true
Rectangle { Rectangle {
@@ -534,12 +455,7 @@ PanelWindow {
transform: Translate { transform: Translate {
id: tx id: tx
x: { x: Anims.slidePx
if (isTopCenter) return 0
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
y: isTopCenter ? -Anims.slidePx : 0
} }
} }
@@ -547,23 +463,15 @@ PanelWindow {
id: enterX id: enterX
target: tx target: tx
property: isTopCenter ? "y" : "x" property: "x"
from: { from: Anims.slidePx
if (isTopCenter) return -Anims.slidePx
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
to: 0 to: 0
duration: Anims.durMed duration: Anims.durMed
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: isTopCenter ? Anims.standardDecel : Anims.emphasizedDecel easing.bezierCurve: Anims.emphasizedDecel
onStopped: { onStopped: {
if (!win.exiting && !win._isDestroying) { if (!win.exiting && !win._isDestroying && Math.abs(tx.x) < 0.5) {
if (isTopCenter) { win.entered()
if (Math.abs(tx.y) < 0.5) win.entered()
} else {
if (Math.abs(tx.x) < 0.5) win.entered()
}
} }
} }
} }
@@ -575,13 +483,9 @@ PanelWindow {
PropertyAnimation { PropertyAnimation {
target: tx target: tx
property: isTopCenter ? "y" : "x" property: "x"
from: 0 from: 0
to: { to: Anims.slidePx
if (isTopCenter) return -Anims.slidePx
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
duration: Anims.durShort duration: Anims.durShort
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasizedAccel easing.bezierCurve: Anims.emphasizedAccel

View File

@@ -12,24 +12,12 @@ DankOSD {
enableMouseInteraction: true enableMouseInteraction: true
Connections { Connections {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null target: AudioService
function onVolumeChanged() { function onVolumeChanged() {
if (!AudioService.suppressOSD) { root.show()
root.show()
}
} }
function onMutedChanged() {
if (!AudioService.suppressOSD) {
root.show()
}
}
}
Connections {
target: AudioService
function onSinkChanged() { function onSinkChanged() {
if (root.shouldBeVisible) { if (root.shouldBeVisible) {
root.show() root.show()

View File

@@ -1,54 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property var axis: null
property string section: "center"
property var popoutTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
property bool isVerticalOrientation: false
property alias content: contentLoader.sourceComponent
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal clicked()
width: isVerticalOrientation ? widgetThickness : contentLoader.item ? (contentLoader.item.implicitWidth + horizontalPadding * 2) : 0
height: isVerticalOrientation ? (contentLoader.item ? (contentLoader.item.implicitHeight + horizontalPadding * 2) : 0) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent"
}
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
}
Loader {
id: contentLoader
anchors.centerIn: parent
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.clicked()
}
}
}

Some files were not shown because too many files have changed in this diff Show More