1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Allow solid colored wallpaper, fix fzf search

This commit is contained in:
bbedward
2025-09-03 16:29:47 -04:00
parent 3856ce14cd
commit b4e607e2b4
27 changed files with 639 additions and 427 deletions

View File

@@ -1,4 +1,5 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtCore import QtCore
@@ -53,19 +54,13 @@ Singleton {
var settings = JSON.parse(content) var settings = JSON.parse(content)
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false
wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : "" wallpaperPath = settings.wallpaperPath !== undefined ? settings.wallpaperPath : ""
wallpaperLastPath = settings.wallpaperLastPath wallpaperLastPath = settings.wallpaperLastPath !== undefined ? settings.wallpaperLastPath : ""
!== undefined ? settings.wallpaperLastPath : "" profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : ""
profileLastPath = settings.profileLastPath
!== undefined ? settings.profileLastPath : ""
doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
nightModeEnabled = settings.nightModeEnabled nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
!== undefined ? settings.nightModeEnabled : false nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
nightModeTemperature = settings.nightModeTemperature nightModeAutoEnabled = settings.nightModeAutoEnabled !== undefined ? settings.nightModeAutoEnabled : false
!== undefined ? settings.nightModeTemperature : 4500 nightModeAutoMode = settings.nightModeAutoMode !== undefined ? settings.nightModeAutoMode : "time"
nightModeAutoEnabled = settings.nightModeAutoEnabled
!== undefined ? settings.nightModeAutoEnabled : false
nightModeAutoMode = settings.nightModeAutoMode
!== undefined ? settings.nightModeAutoMode : "time"
// Handle legacy time format // Handle legacy time format
if (settings.nightModeStartTime !== undefined) { if (settings.nightModeStartTime !== undefined) {
const parts = settings.nightModeStartTime.split(":") const parts = settings.nightModeStartTime.split(":")
@@ -87,24 +82,15 @@ Singleton {
longitude = settings.longitude !== undefined ? settings.longitude : 0.0 longitude = settings.longitude !== undefined ? settings.longitude : 0.0
nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : "" nightModeLocationProvider = settings.nightModeLocationProvider !== undefined ? settings.nightModeLocationProvider : ""
pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : [] pinnedApps = settings.pinnedApps !== undefined ? settings.pinnedApps : []
selectedGpuIndex = settings.selectedGpuIndex selectedGpuIndex = settings.selectedGpuIndex !== undefined ? settings.selectedGpuIndex : 0
!== undefined ? settings.selectedGpuIndex : 0 nvidiaGpuTempEnabled = settings.nvidiaGpuTempEnabled !== undefined ? settings.nvidiaGpuTempEnabled : false
nvidiaGpuTempEnabled = settings.nvidiaGpuTempEnabled nonNvidiaGpuTempEnabled = settings.nonNvidiaGpuTempEnabled !== undefined ? settings.nonNvidiaGpuTempEnabled : false
!== undefined ? settings.nvidiaGpuTempEnabled : false enabledGpuPciIds = settings.enabledGpuPciIds !== undefined ? settings.enabledGpuPciIds : []
nonNvidiaGpuTempEnabled = settings.nonNvidiaGpuTempEnabled wallpaperCyclingEnabled = settings.wallpaperCyclingEnabled !== undefined ? settings.wallpaperCyclingEnabled : false
!== undefined ? settings.nonNvidiaGpuTempEnabled : false wallpaperCyclingMode = settings.wallpaperCyclingMode !== undefined ? settings.wallpaperCyclingMode : "interval"
enabledGpuPciIds = settings.enabledGpuPciIds wallpaperCyclingInterval = settings.wallpaperCyclingInterval !== undefined ? settings.wallpaperCyclingInterval : 300
!== undefined ? settings.enabledGpuPciIds : [] wallpaperCyclingTime = settings.wallpaperCyclingTime !== undefined ? settings.wallpaperCyclingTime : "06:00"
wallpaperCyclingEnabled = settings.wallpaperCyclingEnabled lastBrightnessDevice = settings.lastBrightnessDevice !== undefined ? settings.lastBrightnessDevice : ""
!== undefined ? settings.wallpaperCyclingEnabled : false
wallpaperCyclingMode = settings.wallpaperCyclingMode
!== undefined ? settings.wallpaperCyclingMode : "interval"
wallpaperCyclingInterval = settings.wallpaperCyclingInterval
!== undefined ? settings.wallpaperCyclingInterval : 300
wallpaperCyclingTime = settings.wallpaperCyclingTime
!== undefined ? settings.wallpaperCyclingTime : "06:00"
lastBrightnessDevice = settings.lastBrightnessDevice
!== undefined ? settings.lastBrightnessDevice : ""
notepadContent = settings.notepadContent !== undefined ? settings.notepadContent : "" notepadContent = settings.notepadContent !== undefined ? settings.notepadContent : ""
} }
} catch (e) { } catch (e) {
@@ -218,22 +204,41 @@ Singleton {
} }
function setWallpaper(imagePath) { function setWallpaper(imagePath) {
console.log("SessionData.setWallpaper called with:", imagePath)
wallpaperPath = imagePath wallpaperPath = imagePath
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
console.log("Theme is available, current theme:", Theme.currentTheme)
// Always extract colors for shell UI if dynamic theming is enabled
if (typeof SettingsData !== "undefined" && SettingsData.wallpaperDynamicTheming) { if (typeof SettingsData !== "undefined" && SettingsData.wallpaperDynamicTheming) {
console.log("Dynamic theming enabled, extracting colors") Theme.switchTheme("dynamic")
Theme.extractColors() Theme.extractColors()
} }
// Always generate system themes (matugen templates) when wallpaper changes
console.log("Calling generateSystemThemesFromCurrentTheme")
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} else { }
console.log("Theme is undefined!") }
function setWallpaperColor(color) {
wallpaperPath = color
saveSettings()
if (typeof Theme !== "undefined") {
if (typeof SettingsData !== "undefined" && SettingsData.wallpaperDynamicTheming) {
Theme.switchTheme("dynamic")
Theme.extractColors()
}
Theme.generateSystemThemesFromCurrentTheme()
}
}
function clearWallpaper() {
wallpaperPath = ""
saveSettings()
if (typeof Theme !== "undefined") {
if (typeof SettingsData !== "undefined" && SettingsData.theme) {
Theme.switchTheme(SettingsData.theme)
} else {
Theme.switchTheme("blue")
}
} }
} }
@@ -321,8 +326,7 @@ Singleton {
FileView { FileView {
id: settingsFile id: settingsFile
path: StandardPaths.writableLocation( path: StandardPaths.writableLocation(StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json"
StandardPaths.GenericStateLocation) + "/DankMaterialShell/session.json"
blockLoading: true blockLoading: true
blockWrites: true blockWrites: true
watchChanges: true watchChanges: true
@@ -344,9 +348,7 @@ Singleton {
return "ERROR: No path provided" return "ERROR: No path provided"
} }
var absolutePath = path.startsWith( var absolutePath = path.startsWith("/") ? path : StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/" + path
"/") ? path : StandardPaths.writableLocation(
StandardPaths.HomeLocation) + "/" + path
try { try {
root.setWallpaper(absolutePath) root.setWallpaper(absolutePath)

View File

@@ -1,4 +1,5 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtCore import QtCore
@@ -14,7 +15,7 @@ Singleton {
property string currentTheme: "blue" property string currentTheme: "blue"
property bool isLightMode: false property bool isLightMode: false
readonly property string dynamic: "dynamic" readonly property string dynamic: "dynamic"
readonly property string homeDir: { readonly property string homeDir: {
@@ -27,7 +28,7 @@ Singleton {
} }
readonly property string shellDir: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Common/", "") readonly property string shellDir: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Common/", "")
readonly property string wallpaperPath: typeof SessionData !== "undefined" ? SessionData.wallpaperPath : "" readonly property string wallpaperPath: typeof SessionData !== "undefined" ? SessionData.wallpaperPath : ""
property bool matugenAvailable: false property bool matugenAvailable: false
property bool gtkThemingEnabled: typeof SettingsData !== "undefined" ? SettingsData.gtkAvailable : false property bool gtkThemingEnabled: typeof SettingsData !== "undefined" ? SettingsData.gtkAvailable : false
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
@@ -36,7 +37,7 @@ Singleton {
property bool extractionRequested: false property bool extractionRequested: false
property int colorUpdateTrigger: 0 property int colorUpdateTrigger: 0
property var customThemeData: null property var customThemeData: null
readonly property string stateDir: { readonly property string stateDir: {
const cacheHome = StandardPaths.writableLocation(StandardPaths.CacheLocation).toString() const cacheHome = StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()
const path = cacheHome.startsWith("file://") ? cacheHome.substring(7) : cacheHome const path = cacheHome.startsWith("file://") ? cacheHome.substring(7) : cacheHome
@@ -60,24 +61,24 @@ Singleton {
return customThemeData || StockThemes.getThemeByName("blue", isLightMode) return customThemeData || StockThemes.getThemeByName("blue", isLightMode)
} else if (currentTheme === dynamic) { } else if (currentTheme === dynamic) {
return { return {
primary: getMatugenColor("primary", "#42a5f5"), "primary": getMatugenColor("primary", "#42a5f5"),
primaryText: getMatugenColor("on_primary", "#ffffff"), "primaryText": getMatugenColor("on_primary", "#ffffff"),
primaryContainer: getMatugenColor("primary_container", "#1976d2"), "primaryContainer": getMatugenColor("primary_container", "#1976d2"),
secondary: getMatugenColor("secondary", "#8ab4f8"), "secondary": getMatugenColor("secondary", "#8ab4f8"),
surface: getMatugenColor("surface", "#1a1c1e"), "surface": getMatugenColor("surface", "#1a1c1e"),
surfaceText: getMatugenColor("on_background", "#e3e8ef"), "surfaceText": getMatugenColor("on_background", "#e3e8ef"),
surfaceVariant: getMatugenColor("surface_variant", "#44464f"), "surfaceVariant": getMatugenColor("surface_variant", "#44464f"),
surfaceVariantText: getMatugenColor("on_surface_variant", "#c4c7c5"), "surfaceVariantText": getMatugenColor("on_surface_variant", "#c4c7c5"),
surfaceTint: getMatugenColor("surface_tint", "#8ab4f8"), "surfaceTint": getMatugenColor("surface_tint", "#8ab4f8"),
background: getMatugenColor("background", "#1a1c1e"), "background": getMatugenColor("background", "#1a1c1e"),
backgroundText: getMatugenColor("on_background", "#e3e8ef"), "backgroundText": getMatugenColor("on_background", "#e3e8ef"),
outline: getMatugenColor("outline", "#8e918f"), "outline": getMatugenColor("outline", "#8e918f"),
surfaceContainer: getMatugenColor("surface_container", "#1e2023"), "surfaceContainer": getMatugenColor("surface_container", "#1e2023"),
surfaceContainerHigh: getMatugenColor("surface_container_high", "#292b2f"), "surfaceContainerHigh": getMatugenColor("surface_container_high", "#292b2f"),
error: "#F2B8B5", "error": "#F2B8B5",
warning: "#FF9800", "warning": "#FF9800",
info: "#2196F3", "info": "#2196F3",
success: "#4CAF50" "success": "#4CAF50"
} }
} else { } else {
return StockThemes.getThemeByName(currentTheme, isLightMode) return StockThemes.getThemeByName(currentTheme, isLightMode)
@@ -175,7 +176,7 @@ Singleton {
} }
if (savePrefs && typeof SettingsData !== "undefined") if (savePrefs && typeof SettingsData !== "undefined")
SettingsData.setTheme(currentTheme) SettingsData.setTheme(currentTheme)
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
@@ -183,14 +184,14 @@ Singleton {
isLightMode = light isLightMode = light
if (savePrefs && typeof SessionData !== "undefined") if (savePrefs && typeof SessionData !== "undefined")
SessionData.setLightMode(isLightMode) SessionData.setLightMode(isLightMode)
PortalService.setLightMode(isLightMode) PortalService.setLightMode(isLightMode)
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
function toggleLightMode(savePrefs = true) { function toggleLightMode(savePrefs = true) {
setLightMode(!isLightMode, savePrefs) setLightMode(!isLightMode, savePrefs)
} }
function forceGenerateSystemThemes() { function forceGenerateSystemThemes() {
if (!matugenAvailable) { if (!matugenAvailable) {
if (typeof ToastService !== "undefined") { if (typeof ToastService !== "undefined") {
@@ -225,7 +226,7 @@ Singleton {
} else { } else {
customThemeData = themeData customThemeData = themeData
} }
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
@@ -237,7 +238,6 @@ Singleton {
readonly property var _availableThemeNames: StockThemes.getAllThemeNames() readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
property string currentThemeName: currentTheme property string currentThemeName: currentTheme
function popupBackground() { function popupBackground() {
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, popupTransparency) return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, popupTransparency)
} }
@@ -271,21 +271,34 @@ Singleton {
return _getBatteryPowerProfileIcon() return _getBatteryPowerProfileIcon()
if (isCharging) { if (isCharging) {
if (level >= 90) return "battery_charging_full" if (level >= 90)
if (level >= 80) return "battery_charging_90" return "battery_charging_full"
if (level >= 60) return "battery_charging_80" if (level >= 80)
if (level >= 50) return "battery_charging_60" return "battery_charging_90"
if (level >= 30) return "battery_charging_50" if (level >= 60)
if (level >= 20) return "battery_charging_30" return "battery_charging_80"
if (level >= 50)
return "battery_charging_60"
if (level >= 30)
return "battery_charging_50"
if (level >= 20)
return "battery_charging_30"
return "battery_charging_20" return "battery_charging_20"
} else { } else {
if (level >= 95) return "battery_full" if (level >= 95)
if (level >= 85) return "battery_6_bar" return "battery_full"
if (level >= 70) return "battery_5_bar" if (level >= 85)
if (level >= 55) return "battery_4_bar" return "battery_6_bar"
if (level >= 40) return "battery_3_bar" if (level >= 70)
if (level >= 25) return "battery_2_bar" return "battery_5_bar"
if (level >= 10) return "battery_1_bar" if (level >= 55)
return "battery_4_bar"
if (level >= 40)
return "battery_3_bar"
if (level >= 25)
return "battery_2_bar"
if (level >= 10)
return "battery_1_bar"
return "battery_alert" return "battery_alert"
} }
} }
@@ -355,11 +368,11 @@ Singleton {
if (matugenColors && Object.keys(matugenColors).length > 0) { if (matugenColors && Object.keys(matugenColors).length > 0) {
colorUpdateTrigger++ colorUpdateTrigger++
} }
if (currentTheme === "custom" && customThemeFileView.path) { if (currentTheme === "custom" && customThemeFileView.path) {
customThemeFileView.reload() customThemeFileView.reload()
} }
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
@@ -368,38 +381,39 @@ Singleton {
console.warn("matugen not available - cannot set system theme") console.warn("matugen not available - cannot set system theme")
return return
} }
const desired = { const desired = {
"kind": kind, "kind": kind,
"value": value, "value": value,
"mode": isLight ? "light" : "dark", "mode": isLight ? "light" : "dark",
"iconTheme": iconTheme || "System Default" "iconTheme": iconTheme || "System Default"
} }
const json = JSON.stringify(desired) const json = JSON.stringify(desired)
const desiredPath = stateDir + "/matugen.desired.json" const desiredPath = stateDir + "/matugen.desired.json"
Quickshell.execDetached([ Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
"sh", "-c",
`mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`
])
workerRunning = true workerRunning = true
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"] systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"]
systemThemeGenerator.running = true systemThemeGenerator.running = true
} }
function generateSystemThemesFromCurrentTheme() { function generateSystemThemesFromCurrentTheme() {
if (!matugenAvailable) if (!matugenAvailable)
return return
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"
if (currentTheme === dynamic) { if (currentTheme === dynamic) {
if (!wallpaperPath) { if (!wallpaperPath) {
return return
} }
setDesiredTheme("image", wallpaperPath, isLight, iconTheme) if (wallpaperPath.startsWith("#")) {
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme)
} else {
setDesiredTheme("image", wallpaperPath, isLight, iconTheme)
}
} else { } else {
let primaryColor let primaryColor
if (currentTheme === "custom") { if (currentTheme === "custom") {
@@ -411,7 +425,7 @@ Singleton {
} else { } else {
primaryColor = currentThemeData.primary primaryColor = currentThemeData.primary
} }
if (!primaryColor) { if (!primaryColor) {
console.warn("No primary color available for theme:", currentTheme) console.warn("No primary color available for theme:", currentTheme)
return return
@@ -445,17 +459,22 @@ Singleton {
qtApplier.running = true qtApplier.running = true
} }
function extractJsonFromText(text) { function extractJsonFromText(text) {
if (!text) return null if (!text)
return null
const start = text.search(/[{\[]/) const start = text.search(/[{\[]/)
if (start === -1) return null if (start === -1)
return null
const open = text[start] const open = text[start]
const pairs = { "{": '}', "[": ']' } const pairs = {
"{": '}',
"[": ']'
}
const close = pairs[open] const close = pairs[open]
if (!close) return null if (!close)
return null
let inString = false let inString = false
let escape = false let escape = false
@@ -511,17 +530,18 @@ Singleton {
if (extractionRequested) { if (extractionRequested) {
fileChecker.running = true 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"
if (currentTheme === dynamic) { if (currentTheme === dynamic) {
if (wallpaperPath) { if (wallpaperPath) {
// Clear cache on startup to force regeneration
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"]) Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
setDesiredTheme("image", wallpaperPath, isLight, iconTheme) if (wallpaperPath.startsWith("#")) {
} else { setDesiredTheme("hex", wallpaperPath, isLight, iconTheme)
} else {
setDesiredTheme("image", wallpaperPath, isLight, iconTheme)
}
} }
} else { } else {
let primaryColor let primaryColor
@@ -532,12 +552,10 @@ Singleton {
} else { } else {
primaryColor = currentThemeData.primary primaryColor = currentThemeData.primary
} }
if (primaryColor) { if (primaryColor) {
// Clear cache on startup to force regeneration
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"]) Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
setDesiredTheme("hex", primaryColor, isLight, iconTheme) setDesiredTheme("hex", primaryColor, isLight, iconTheme)
} else {
} }
} }
} }
@@ -549,6 +567,8 @@ Singleton {
onExited: code => { onExited: code => {
if (code === 0) { if (code === 0) {
matugenProcess.running = true matugenProcess.running = true
} else if (wallpaperPath.startsWith("#")) {
colorMatugenProcess.running = true
} }
} }
} }
@@ -601,18 +621,65 @@ Singleton {
} }
} }
Process {
id: colorMatugenProcess
command: ["matugen", "color", "hex", wallpaperPath, "--json", "hex"]
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
} }
Process { Process {
id: systemThemeGenerator id: systemThemeGenerator
running: false running: false
onExited: exitCode => { onExited: exitCode => {
workerRunning = false workerRunning = false
if (exitCode === 2) { if (exitCode === 2) {
// Exit code 2 means wallpaper/color not found - this is expected on first run // 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") console.log("Theme worker: wallpaper/color not found, skipping theme generation")
@@ -675,14 +742,10 @@ Singleton {
} }
} }
Component.onCompleted: { Component.onCompleted: {
matugenCheck.running = true matugenCheck.running = true
if (typeof SessionData !== "undefined") if (typeof SessionData !== "undefined")
SessionData.isLightModeChanged.connect(root.onLightModeChanged) SessionData.isLightModeChanged.connect(root.onLightModeChanged)
} }
FileView { FileView {
@@ -697,7 +760,7 @@ Singleton {
ToastService.showError("Invalid JSON format: " + e.message) ToastService.showError("Invalid JSON format: " + e.message)
} }
} }
onLoaded: { onLoaded: {
parseAndLoadTheme() parseAndLoadTheme()
} }
@@ -706,7 +769,7 @@ Singleton {
customThemeFileView.reload() customThemeFileView.reload()
} }
onLoadFailed: function(error) { onLoadFailed: function (error) {
if (typeof ToastService !== "undefined") { if (typeof ToastService !== "undefined") {
ToastService.showError("Failed to read theme file: " + error) ToastService.showError("Failed to read theme file: " + error)
} }
@@ -735,4 +798,4 @@ Singleton {
return root.isLightMode ? "light" : "dark" return root.isLightMode ? "light" : "dark"
} }
} }
} }

View File

@@ -1,4 +1,4 @@
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls

View File

@@ -23,7 +23,7 @@ Item {
property string powerDialogMessage: "" property string powerDialogMessage: ""
property string powerDialogConfirmText: "" property string powerDialogConfirmText: ""
property color powerDialogConfirmColor: Theme.primary property color powerDialogConfirmColor: Theme.primary
property var powerDialogOnConfirm: function() {} property var powerDialogOnConfirm: function () {}
function showPowerDialog(title, message, confirmText, confirmColor, onConfirm) { function showPowerDialog(title, message, confirmText, confirmColor, onConfirm) {
powerDialogTitle = title powerDialogTitle = title
@@ -57,7 +57,7 @@ Item {
Loader { Loader {
anchors.fill: parent anchors.fill: parent
active: !SessionData.wallpaperPath active: !SessionData.wallpaperPath || (SessionData.wallpaperPath && SessionData.wallpaperPath.startsWith("#"))
asynchronous: true asynchronous: true
sourceComponent: DankBackdrop {} sourceComponent: DankBackdrop {}
@@ -67,7 +67,7 @@ Item {
id: wallpaperBackground id: wallpaperBackground
anchors.fill: parent anchors.fill: parent
source: SessionData.wallpaperPath || "" source: (SessionData.wallpaperPath && !SessionData.wallpaperPath.startsWith("#")) ? SessionData.wallpaperPath : ""
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
smooth: true smooth: true
asynchronous: false asynchronous: false
@@ -802,7 +802,7 @@ Item {
if (demoMode) { if (demoMode) {
console.log("Demo: Power") console.log("Demo: Power")
} else { } else {
showPowerDialog("Power Off", "Power off this computer?", "Power Off", Theme.error, function() { showPowerDialog("Power Off", "Power off this computer?", "Power Off", Theme.error, function () {
SessionService.poweroff() SessionService.poweroff()
}) })
} }
@@ -816,7 +816,7 @@ Item {
if (demoMode) { if (demoMode) {
console.log("Demo: Reboot") console.log("Demo: Reboot")
} else { } else {
showPowerDialog("Restart", "Restart this computer?", "Restart", Theme.primary, function() { showPowerDialog("Restart", "Restart this computer?", "Restart", Theme.primary, function () {
SessionService.reboot() SessionService.reboot()
}) })
} }
@@ -830,7 +830,7 @@ Item {
if (demoMode) { if (demoMode) {
console.log("Demo: Logout") console.log("Demo: Logout")
} else { } else {
showPowerDialog("Log Out", "End this session?", "Log Out", Theme.primary, function() { showPowerDialog("Log Out", "End this session?", "Log Out", Theme.primary, function () {
SessionService.logout() SessionService.logout()
}) })
} }

View File

@@ -18,56 +18,58 @@ Item {
property bool fontsEnumerated: false property bool fontsEnumerated: false
function enumerateFonts() { function enumerateFonts() {
var fonts = ["Default"]; var fonts = ["Default"]
var availableFonts = Qt.fontFamilies(); var availableFonts = Qt.fontFamilies()
var rootFamilies = []; var rootFamilies = []
var seenFamilies = new Set(); var seenFamilies = new Set()
for (var i = 0; i < availableFonts.length; i++) { for (var i = 0; i < availableFonts.length; i++) {
var fontName = availableFonts[i]; var fontName = availableFonts[i]
if (fontName.startsWith(".")) if (fontName.startsWith("."))
continue; continue
if (fontName === SettingsData.defaultFontFamily) if (fontName === SettingsData.defaultFontFamily)
continue; continue
var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function(match, suffix) { var rootName = fontName.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i,
return match; "").replace(/ (UI|Display|Text|Mono|Sans|Serif)$/i, function (match, suffix) {
}).trim(); return match
}).trim()
if (!seenFamilies.has(rootName) && rootName !== "") { if (!seenFamilies.has(rootName) && rootName !== "") {
seenFamilies.add(rootName); seenFamilies.add(rootName)
rootFamilies.push(rootName); rootFamilies.push(rootName)
} }
} }
cachedFontFamilies = fonts.concat(rootFamilies.sort()); cachedFontFamilies = fonts.concat(rootFamilies.sort())
var monoFonts = ["Default"]; var monoFonts = ["Default"]
var monoFamilies = []; var monoFamilies = []
var seenMonoFamilies = new Set(); var seenMonoFamilies = new Set()
for (var j = 0; j < availableFonts.length; j++) { for (var j = 0; j < availableFonts.length; j++) {
var fontName2 = availableFonts[j]; var fontName2 = availableFonts[j]
if (fontName2.startsWith(".")) if (fontName2.startsWith("."))
continue; continue
if (fontName2 === SettingsData.defaultMonoFontFamily) if (fontName2 === SettingsData.defaultMonoFontFamily)
continue; continue
var lowerName = fontName2.toLowerCase(); var lowerName = fontName2.toLowerCase()
if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes("jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) { if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes(
var rootName2 = fontName2.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim(); "jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) {
var rootName2 = fontName2.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim()
if (!seenMonoFamilies.has(rootName2) && rootName2 !== "") { if (!seenMonoFamilies.has(rootName2) && rootName2 !== "") {
seenMonoFamilies.add(rootName2); seenMonoFamilies.add(rootName2)
monoFamilies.push(rootName2); monoFamilies.push(rootName2)
} }
} }
} }
cachedMonoFamilies = monoFonts.concat(monoFamilies.sort()); cachedMonoFamilies = monoFonts.concat(monoFamilies.sort())
} }
Component.onCompleted: { Component.onCompleted: {
// Access WallpaperCyclingService to ensure it's initialized // Access WallpaperCyclingService to ensure it's initialized
WallpaperCyclingService.cyclingActive; WallpaperCyclingService.cyclingActive
if (!fontsEnumerated) { if (!fontsEnumerated) {
enumerateFonts(); enumerateFonts()
fontsEnumerated = true; fontsEnumerated = true
} }
} }
@@ -118,7 +120,6 @@ Item {
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
Row { Row {
@@ -136,9 +137,9 @@ Item {
CachingImage { CachingImage {
anchors.fill: parent anchors.fill: parent
anchors.margins: 1 anchors.margins: 1
source: SessionData.wallpaperPath !== "" ? "file://" + SessionData.wallpaperPath : "" source: (SessionData.wallpaperPath !== "" && !SessionData.wallpaperPath.startsWith("#")) ? "file://" + SessionData.wallpaperPath : ""
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: SessionData.wallpaperPath !== "" visible: SessionData.wallpaperPath !== "" && !SessionData.wallpaperPath.startsWith("#")
maxCacheSize: 160 maxCacheSize: 160
layer.enabled: true layer.enabled: true
@@ -148,7 +149,14 @@ Item {
maskThresholdMin: 0.5 maskThresholdMin: 0.5
maskSpreadAtMin: 1 maskSpreadAtMin: 1
} }
}
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: Theme.cornerRadius - 1
color: SessionData.wallpaperPath.startsWith("#") ? SessionData.wallpaperPath : "transparent"
visible: SessionData.wallpaperPath !== "" && SessionData.wallpaperPath.startsWith("#")
} }
Rectangle { Rectangle {
@@ -199,13 +207,34 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (parentModal) { if (parentModal) {
parentModal.allowFocusOverride = true; parentModal.allowFocusOverride = true
parentModal.shouldHaveFocus = false; parentModal.shouldHaveFocus = false
} }
wallpaperBrowser.open(); wallpaperBrowser.open()
} }
} }
}
Rectangle {
width: 32
height: 32
radius: 16
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "palette"
size: 18
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
colorPicker.open()
}
}
} }
Rectangle { Rectangle {
@@ -227,16 +256,13 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (Theme.currentTheme === Theme.dynamic) if (Theme.currentTheme === Theme.dynamic)
Theme.switchTheme("blue"); Theme.switchTheme("blue")
SessionData.setWallpaper(""); SessionData.clearWallpaper()
} }
} }
} }
} }
} }
MouseArea { MouseArea {
@@ -248,7 +274,6 @@ Item {
propagateComposedEvents: true propagateComposedEvents: true
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
} }
} }
Column { Column {
@@ -283,12 +308,12 @@ Item {
buttonSize: 32 buttonSize: 32
iconName: "skip_previous" iconName: "skip_previous"
iconSize: Theme.iconSizeSmall iconSize: Theme.iconSizeSmall
enabled: SessionData.wallpaperPath enabled: SessionData.wallpaperPath && !SessionData.wallpaperPath.startsWith("#")
opacity: SessionData.wallpaperPath ? 1 : 0.5 opacity: (SessionData.wallpaperPath && !SessionData.wallpaperPath.startsWith("#")) ? 1 : 0.5
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: { onClicked: {
WallpaperCyclingService.cyclePrevManually(); WallpaperCyclingService.cyclePrevManually()
} }
} }
@@ -296,19 +321,16 @@ Item {
buttonSize: 32 buttonSize: 32
iconName: "skip_next" iconName: "skip_next"
iconSize: Theme.iconSizeSmall iconSize: Theme.iconSizeSmall
enabled: SessionData.wallpaperPath enabled: SessionData.wallpaperPath && !SessionData.wallpaperPath.startsWith("#")
opacity: SessionData.wallpaperPath ? 1 : 0.5 opacity: (SessionData.wallpaperPath && !SessionData.wallpaperPath.startsWith("#")) ? 1 : 0.5
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
onClicked: { onClicked: {
WallpaperCyclingService.cycleNextManually(); WallpaperCyclingService.cycleNextManually()
} }
} }
} }
} }
} }
// Wallpaper Cycling Section - Full Width // Wallpaper Cycling Section - Full Width
@@ -354,7 +376,6 @@ Item {
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
width: parent.width width: parent.width
} }
} }
DankToggle { DankToggle {
@@ -362,11 +383,10 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: SessionData.wallpaperCyclingEnabled checked: SessionData.wallpaperCyclingEnabled
onToggled: (toggled) => { onToggled: toggled => {
return SessionData.setWallpaperCyclingEnabled(toggled); return SessionData.setWallpaperCyclingEnabled(toggled)
} }
} }
} }
// Cycling mode and settings // Cycling mode and settings
@@ -393,18 +413,17 @@ Item {
width: 200 width: 200
height: 32 height: 32
model: [{ model: [{
"text": "Interval", "text": "Interval",
"icon": "schedule" "icon": "schedule"
}, { }, {
"text": "Time", "text": "Time",
"icon": "access_time" "icon": "access_time"
}] }]
currentIndex: SessionData.wallpaperCyclingMode === "time" ? 1 : 0 currentIndex: SessionData.wallpaperCyclingMode === "time" ? 1 : 0
onTabClicked: (index) => { onTabClicked: index => {
SessionData.setWallpaperCyclingMode(index === 1 ? "time" : "interval"); SessionData.setWallpaperCyclingMode(index === 1 ? "time" : "interval")
} }
} }
} }
// Interval settings // Interval settings
@@ -418,16 +437,15 @@ Item {
description: "How often to change wallpaper" description: "How often to change wallpaper"
options: intervalOptions options: intervalOptions
currentValue: { currentValue: {
const currentSeconds = SessionData.wallpaperCyclingInterval; const currentSeconds = SessionData.wallpaperCyclingInterval
const index = intervalValues.indexOf(currentSeconds); const index = intervalValues.indexOf(currentSeconds)
return index >= 0 ? intervalOptions[index] : "5 minutes"; return index >= 0 ? intervalOptions[index] : "5 minutes"
}
onValueChanged: (value) => {
const index = intervalOptions.indexOf(value);
if (index >= 0)
SessionData.setWallpaperCyclingInterval(intervalValues[index]);
} }
onValueChanged: value => {
const index = intervalOptions.indexOf(value)
if (index >= 0)
SessionData.setWallpaperCyclingInterval(intervalValues[index])
}
} }
// Time settings // Time settings
@@ -452,25 +470,24 @@ Item {
topPadding: Theme.spacingS topPadding: Theme.spacingS
bottomPadding: Theme.spacingS bottomPadding: Theme.spacingS
onAccepted: { onAccepted: {
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text); var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
if (isValid) if (isValid)
SessionData.setWallpaperCyclingTime(text); SessionData.setWallpaperCyclingTime(text)
else else
text = SessionData.wallpaperCyclingTime; text = SessionData.wallpaperCyclingTime
} }
onEditingFinished: { onEditingFinished: {
var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text); var isValid = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/.test(text)
if (isValid) if (isValid)
SessionData.setWallpaperCyclingTime(text); SessionData.setWallpaperCyclingTime(text)
else else
text = SessionData.wallpaperCyclingTime; text = SessionData.wallpaperCyclingTime
} }
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
validator: RegularExpressionValidator { validator: RegularExpressionValidator {
regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/ regularExpression: /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/
} }
} }
StyledText { StyledText {
@@ -479,15 +496,10 @@ Item {
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
} }
} }
} }
} }
// Dynamic Theme Section // Dynamic Theme Section
@@ -536,7 +548,6 @@ Item {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
} }
} }
DankToggle { DankToggle {
@@ -545,14 +556,13 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: Theme.wallpaperPath !== "" && Theme.currentTheme === Theme.dynamic checked: Theme.wallpaperPath !== "" && Theme.currentTheme === Theme.dynamic
enabled: ToastService.wallpaperErrorStatus !== "matugen_missing" && Theme.wallpaperPath !== "" enabled: ToastService.wallpaperErrorStatus !== "matugen_missing" && Theme.wallpaperPath !== ""
onToggled: (toggled) => { onToggled: toggled => {
if (toggled) if (toggled)
Theme.switchTheme(Theme.dynamic); Theme.switchTheme(Theme.dynamic)
else else
Theme.switchTheme("blue"); Theme.switchTheme("blue")
} }
} }
} }
StyledText { StyledText {
@@ -563,9 +573,7 @@ Item {
width: parent.width width: parent.width
leftPadding: Theme.iconSize + Theme.spacingM leftPadding: Theme.iconSize + Theme.spacingM
} }
} }
} }
// Display Settings // Display Settings
@@ -602,7 +610,6 @@ Item {
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
DankToggle { DankToggle {
@@ -610,9 +617,9 @@ Item {
text: "Light Mode" text: "Light Mode"
description: "Use light theme instead of dark theme" description: "Use light theme instead of dark theme"
checked: SessionData.isLightMode checked: SessionData.isLightMode
onToggled: (checked) => { onToggled: checked => {
Theme.setLightMode(checked); Theme.setLightMode(checked)
} }
} }
Rectangle { Rectangle {
@@ -629,13 +636,13 @@ Item {
text: "Night Mode" text: "Night Mode"
description: "Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates." description: "Apply warm color temperature to reduce eye strain. Use automation settings below to control when it activates."
checked: DisplayService.nightModeEnabled checked: DisplayService.nightModeEnabled
onToggled: (checked) => { onToggled: checked => {
DisplayService.toggleNightMode(); DisplayService.toggleNightMode()
} }
Connections { Connections {
function onNightModeEnabledChanged() { function onNightModeEnabledChanged() {
nightModeToggle.checked = DisplayService.nightModeEnabled; nightModeToggle.checked = DisplayService.nightModeEnabled
} }
target: DisplayService target: DisplayService
@@ -648,16 +655,16 @@ Item {
description: "Color temperature for night mode" description: "Color temperature for night mode"
currentValue: SessionData.nightModeTemperature + "K" currentValue: SessionData.nightModeTemperature + "K"
options: { options: {
var temps = []; var temps = []
for (var i = 2500; i <= 6000; i += 500) { for (var i = 2500; i <= 6000; i += 500) {
temps.push(i + "K"); temps.push(i + "K")
} }
return temps; return temps
}
onValueChanged: (value) => {
var temp = parseInt(value.replace("K", ""));
SessionData.setNightModeTemperature(temp);
} }
onValueChanged: value => {
var temp = parseInt(value.replace("K", ""))
SessionData.setNightModeTemperature(temp)
}
} }
DankToggle { DankToggle {
@@ -666,19 +673,19 @@ Item {
text: "Automatic Control" text: "Automatic Control"
description: "Only adjust gamma based on time or location rules." description: "Only adjust gamma based on time or location rules."
checked: SessionData.nightModeAutoEnabled checked: SessionData.nightModeAutoEnabled
onToggled: (checked) => { onToggled: checked => {
if (checked && !DisplayService.nightModeEnabled) { if (checked && !DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode(); DisplayService.toggleNightMode()
} else if (!checked && DisplayService.nightModeEnabled) { } else if (!checked && DisplayService.nightModeEnabled) {
DisplayService.toggleNightMode(); DisplayService.toggleNightMode()
} }
SessionData.setNightModeAutoEnabled(checked); SessionData.setNightModeAutoEnabled(checked)
} }
Connections { Connections {
target: SessionData target: SessionData
function onNightModeAutoEnabledChanged() { function onNightModeAutoEnabledChanged() {
automaticToggle.checked = SessionData.nightModeAutoEnabled; automaticToggle.checked = SessionData.nightModeAutoEnabled
} }
} }
} }
@@ -693,7 +700,7 @@ Item {
Connections { Connections {
target: SessionData target: SessionData
function onNightModeAutoEnabledChanged() { function onNightModeAutoEnabledChanged() {
automaticSettings.visible = SessionData.nightModeAutoEnabled; automaticSettings.visible = SessionData.nightModeAutoEnabled
} }
} }
@@ -702,22 +709,22 @@ Item {
width: 200 width: 200
height: 32 height: 32
model: [{ model: [{
"text": "Time", "text": "Time",
"icon": "access_time" "icon": "access_time"
}, { }, {
"text": "Location", "text": "Location",
"icon": "place" "icon": "place"
}] }]
Component.onCompleted: { Component.onCompleted: {
currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0; currentIndex = SessionData.nightModeAutoMode === "location" ? 1 : 0
}
onTabClicked: (index) => {
console.log("Tab clicked:", index, "Setting mode to:", index === 1 ? "location" : "time");
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time");
currentIndex = index;
} }
onTabClicked: index => {
console.log("Tab clicked:", index, "Setting mode to:", index === 1 ? "location" : "time")
DisplayService.setNightModeAutomationMode(index === 1 ? "location" : "time")
currentIndex = index
}
} }
Column { Column {
@@ -770,15 +777,15 @@ Item {
text: "" text: ""
currentValue: SessionData.nightModeStartHour.toString() currentValue: SessionData.nightModeStartHour.toString()
options: { options: {
var hours = []; var hours = []
for (var i = 0; i < 24; i++) { for (var i = 0; i < 24; i++) {
hours.push(i.toString()); hours.push(i.toString())
} }
return hours; return hours
}
onValueChanged: (value) => {
SessionData.setNightModeStartHour(parseInt(value));
} }
onValueChanged: value => {
SessionData.setNightModeStartHour(parseInt(value))
}
} }
DankDropdown { DankDropdown {
@@ -787,15 +794,15 @@ Item {
text: "" text: ""
currentValue: SessionData.nightModeStartMinute.toString().padStart(2, '0') currentValue: SessionData.nightModeStartMinute.toString().padStart(2, '0')
options: { options: {
var minutes = []; var minutes = []
for (var i = 0; i < 60; i += 5) { for (var i = 0; i < 60; i += 5) {
minutes.push(i.toString().padStart(2, '0')); minutes.push(i.toString().padStart(2, '0'))
} }
return minutes; return minutes
}
onValueChanged: (value) => {
SessionData.setNightModeStartMinute(parseInt(value));
} }
onValueChanged: value => {
SessionData.setNightModeStartMinute(parseInt(value))
}
} }
} }
@@ -818,15 +825,15 @@ Item {
text: "" text: ""
currentValue: SessionData.nightModeEndHour.toString() currentValue: SessionData.nightModeEndHour.toString()
options: { options: {
var hours = []; var hours = []
for (var i = 0; i < 24; i++) { for (var i = 0; i < 24; i++) {
hours.push(i.toString()); hours.push(i.toString())
} }
return hours; return hours
}
onValueChanged: (value) => {
SessionData.setNightModeEndHour(parseInt(value));
} }
onValueChanged: value => {
SessionData.setNightModeEndHour(parseInt(value))
}
} }
DankDropdown { DankDropdown {
@@ -835,15 +842,15 @@ Item {
text: "" text: ""
currentValue: SessionData.nightModeEndMinute.toString().padStart(2, '0') currentValue: SessionData.nightModeEndMinute.toString().padStart(2, '0')
options: { options: {
var minutes = []; var minutes = []
for (var i = 0; i < 60; i += 5) { for (var i = 0; i < 60; i += 5) {
minutes.push(i.toString().padStart(2, '0')); minutes.push(i.toString().padStart(2, '0'))
} }
return minutes; return minutes
}
onValueChanged: (value) => {
SessionData.setNightModeEndMinute(parseInt(value));
} }
onValueChanged: value => {
SessionData.setNightModeEndMinute(parseInt(value))
}
} }
} }
} }
@@ -860,15 +867,15 @@ Item {
description: DisplayService.geoclueAvailable ? "Use automatic location detection (geoclue2)" : "Geoclue service not running - cannot auto-detect location" description: DisplayService.geoclueAvailable ? "Use automatic location detection (geoclue2)" : "Geoclue service not running - cannot auto-detect location"
checked: SessionData.nightModeLocationProvider === "geoclue2" checked: SessionData.nightModeLocationProvider === "geoclue2"
enabled: DisplayService.geoclueAvailable enabled: DisplayService.geoclueAvailable
onToggled: (checked) => { onToggled: checked => {
if (checked && DisplayService.geoclueAvailable) { if (checked && DisplayService.geoclueAvailable) {
SessionData.setNightModeLocationProvider("geoclue2") SessionData.setNightModeLocationProvider("geoclue2")
SessionData.setLatitude(0.0) SessionData.setLatitude(0.0)
SessionData.setLongitude(0.0) SessionData.setLongitude(0.0)
} else { } else {
SessionData.setNightModeLocationProvider("") SessionData.setNightModeLocationProvider("")
} }
} }
} }
StyledText { StyledText {
@@ -937,13 +944,8 @@ Item {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
} }
} }
} }
// Font Settings // Font Settings
@@ -980,7 +982,6 @@ Item {
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
DankDropdown { DankDropdown {
@@ -989,20 +990,20 @@ Item {
description: "Select system font family" description: "Select system font family"
currentValue: { currentValue: {
if (SettingsData.fontFamily === SettingsData.defaultFontFamily) if (SettingsData.fontFamily === SettingsData.defaultFontFamily)
return "Default"; return "Default"
else else
return SettingsData.fontFamily || "Default"; return SettingsData.fontFamily || "Default"
} }
enableFuzzySearch: true enableFuzzySearch: true
popupWidthOffset: 100 popupWidthOffset: 100
maxPopupHeight: 400 maxPopupHeight: 400
options: cachedFontFamilies options: cachedFontFamilies
onValueChanged: (value) => { onValueChanged: value => {
if (value.startsWith("Default")) if (value.startsWith("Default"))
SettingsData.setFontFamily(SettingsData.defaultFontFamily); SettingsData.setFontFamily(SettingsData.defaultFontFamily)
else else
SettingsData.setFontFamily(value); SettingsData.setFontFamily(value)
} }
} }
DankDropdown { DankDropdown {
@@ -1012,64 +1013,64 @@ Item {
currentValue: { currentValue: {
switch (SettingsData.fontWeight) { switch (SettingsData.fontWeight) {
case Font.Thin: case Font.Thin:
return "Thin"; return "Thin"
case Font.ExtraLight: case Font.ExtraLight:
return "Extra Light"; return "Extra Light"
case Font.Light: case Font.Light:
return "Light"; return "Light"
case Font.Normal: case Font.Normal:
return "Regular"; return "Regular"
case Font.Medium: case Font.Medium:
return "Medium"; return "Medium"
case Font.DemiBold: case Font.DemiBold:
return "Demi Bold"; return "Demi Bold"
case Font.Bold: case Font.Bold:
return "Bold"; return "Bold"
case Font.ExtraBold: case Font.ExtraBold:
return "Extra Bold"; return "Extra Bold"
case Font.Black: case Font.Black:
return "Black"; return "Black"
default: default:
return "Regular"; return "Regular"
} }
} }
options: ["Thin", "Extra Light", "Light", "Regular", "Medium", "Demi Bold", "Bold", "Extra Bold", "Black"] options: ["Thin", "Extra Light", "Light", "Regular", "Medium", "Demi Bold", "Bold", "Extra Bold", "Black"]
onValueChanged: (value) => { onValueChanged: value => {
var weight; var weight
switch (value) { switch (value) {
case "Thin": case "Thin":
weight = Font.Thin; weight = Font.Thin
break; break
case "Extra Light": case "Extra Light":
weight = Font.ExtraLight; weight = Font.ExtraLight
break; break
case "Light": case "Light":
weight = Font.Light; weight = Font.Light
break; break
case "Regular": case "Regular":
weight = Font.Normal; weight = Font.Normal
break; break
case "Medium": case "Medium":
weight = Font.Medium; weight = Font.Medium
break; break
case "Demi Bold": case "Demi Bold":
weight = Font.DemiBold; weight = Font.DemiBold
break; break
case "Bold": case "Bold":
weight = Font.Bold; weight = Font.Bold
break; break
case "Extra Bold": case "Extra Bold":
weight = Font.ExtraBold; weight = Font.ExtraBold
break; break
case "Black": case "Black":
weight = Font.Black; weight = Font.Black
break; break
default: default:
weight = Font.Normal; weight = Font.Normal
break; break
} }
SettingsData.setFontWeight(weight); SettingsData.setFontWeight(weight)
} }
} }
DankDropdown { DankDropdown {
@@ -1078,28 +1079,24 @@ Item {
description: "Select monospace font for process list and technical displays" description: "Select monospace font for process list and technical displays"
currentValue: { currentValue: {
if (SettingsData.monoFontFamily === SettingsData.defaultMonoFontFamily) if (SettingsData.monoFontFamily === SettingsData.defaultMonoFontFamily)
return "Default"; return "Default"
return SettingsData.monoFontFamily || "Default"; return SettingsData.monoFontFamily || "Default"
} }
enableFuzzySearch: true enableFuzzySearch: true
popupWidthOffset: 100 popupWidthOffset: 100
maxPopupHeight: 400 maxPopupHeight: 400
options: cachedMonoFamilies options: cachedMonoFamilies
onValueChanged: (value) => { onValueChanged: value => {
if (value === "Default") if (value === "Default")
SettingsData.setMonoFontFamily(SettingsData.defaultMonoFontFamily); SettingsData.setMonoFontFamily(SettingsData.defaultMonoFontFamily)
else else
SettingsData.setMonoFontFamily(value); SettingsData.setMonoFontFamily(value)
} }
} }
} }
} }
} }
} }
FileBrowserModal { FileBrowserModal {
@@ -1109,18 +1106,26 @@ Item {
browserIcon: "wallpaper" browserIcon: "wallpaper"
browserType: "wallpaper" browserType: "wallpaper"
fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"] fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp"]
onFileSelected: (path) => { onFileSelected: path => {
SessionData.setWallpaper(path); SessionData.setWallpaper(path)
close(); close()
} }
onDialogClosed: { onDialogClosed: {
if (parentModal) { if (parentModal) {
parentModal.allowFocusOverride = false; parentModal.allowFocusOverride = false
parentModal.shouldHaveFocus = Qt.binding(() => { parentModal.shouldHaveFocus = Qt.binding(() => {
return parentModal.shouldBeVisible; return parentModal.shouldBeVisible
}); })
} }
} }
} }
DankColorPicker {
id: colorPicker
pickerTitle: "Choose Wallpaper Color"
onColorSelected: selectedColor => {
SessionData.setWallpaperColor(selectedColor)
}
}
} }

View File

@@ -33,20 +33,42 @@ LazyLoader {
anchors.fill: parent anchors.fill: parent
property string source: SessionData.wallpaperPath || "" property string source: SessionData.wallpaperPath || ""
property bool isColorSource: source.startsWith("#")
property Image current: one property Image current: one
onSourceChanged: { onSourceChanged: {
if (!source) if (!source) {
current = null current = null
else if (current === one) one.source = ""
two.update() two.source = ""
else } else if (isColorSource) {
one.update() current = null
one.source = ""
two.source = ""
} else {
if (current === one)
two.update()
else
one.update()
}
}
onIsColorSourceChanged: {
if (isColorSource) {
current = null
one.source = ""
two.source = ""
} else if (source) {
if (current === one)
two.update()
else
one.update()
}
} }
Loader { Loader {
anchors.fill: parent anchors.fill: parent
active: !root.source active: !root.source || root.isColorSource
asynchronous: true asynchronous: true
sourceComponent: DankBackdrop {} sourceComponent: DankBackdrop {}

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
@@ -29,56 +29,56 @@ Singleton {
let score = 0 let score = 0
let matched = false let matched = false
// Exact name match - highest priority const nameWords = name.trim().split(/\s+/).filter(w => w.length > 0)
const containsAsWord = nameWords.includes(queryLower)
const startsWithAsWord = nameWords.some(word => word.startsWith(queryLower))
if (name === queryLower) { if (name === queryLower) {
score = 1000 score = 10000
matched = true
}
else if (containsAsWord) {
score = 9500 + (100 - Math.min(name.length, 100))
matched = true matched = true
} }
// Name starts with query
else if (name.startsWith(queryLower)) { else if (name.startsWith(queryLower)) {
score = 900 - name.length score = 9000 + (100 - Math.min(name.length, 100))
matched = true matched = true
} }
// Name contains query as a word else if (startsWithAsWord) {
else if (name.includes(" " + queryLower) || name.includes(queryLower + " ")) { score = 8500 + (100 - Math.min(name.length, 100))
score = 800 - name.length
matched = true matched = true
} }
// Name contains query substring
else if (name.includes(queryLower)) { else if (name.includes(queryLower)) {
score = 700 - name.length score = 8000 + (100 - Math.min(name.length, 100))
matched = true matched = true
} }
// Check individual keywords
else if (keywords.length > 0) { else if (keywords.length > 0) {
for (const keyword of keywords) { for (const keyword of keywords) {
if (keyword === queryLower) { if (keyword === queryLower) {
score = 650 // Exact keyword match score = 6000
matched = true matched = true
break break
} else if (keyword.startsWith(queryLower)) { } else if (keyword.startsWith(queryLower)) {
score = 620 // Keyword starts with query score = 5500
matched = true matched = true
break break
} else if (keyword.includes(queryLower)) { } else if (keyword.includes(queryLower)) {
score = 600 // Keyword contains query score = 5000
matched = true matched = true
break break
} }
} }
} }
// Generic name matches
if (!matched && genericName.includes(queryLower)) { if (!matched && genericName.includes(queryLower)) {
score = 500 score = 4000
matched = true matched = true
} }
// Comment contains query
else if (!matched && comment.includes(queryLower)) { else if (!matched && comment.includes(queryLower)) {
score = 400 score = 3000
matched = true matched = true
} }
// Fuzzy match on name only (not on all fields) else if (!matched) {
else {
const nameFinder = new Fzf.Finder([app], { const nameFinder = new Fzf.Finder([app], {
"selector": a => a.name || "", "selector": a => a.name || "",
"casing": "case-insensitive", "casing": "case-insensitive",
@@ -86,7 +86,7 @@ Singleton {
}) })
const fuzzyResults = nameFinder.find(query) const fuzzyResults = nameFinder.find(query)
if (fuzzyResults.length > 0 && fuzzyResults[0].score > 0) { if (fuzzyResults.length > 0 && fuzzyResults[0].score > 0) {
score = fuzzyResults[0].score score = Math.min(fuzzyResults[0].score, 2000)
matched = true matched = true
} }
} }
@@ -96,10 +96,7 @@ Singleton {
} }
} }
// Sort by score descending
scoredApps.sort((a, b) => b.score - a.score) scoredApps.sort((a, b) => b.score - a.score)
// Return top results
return scoredApps.slice(0, 50).map(item => item.app) return scoredApps.slice(0, 50).map(item => item.app)
} }

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -1,6 +1,6 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell

View File

@@ -6,9 +6,11 @@ Item {
anchors.fill: parent anchors.fill: parent
property bool isColorWallpaper: SessionData.wallpaperPath && SessionData.wallpaperPath.startsWith("#")
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: Theme.background color: isColorWallpaper ? SessionData.wallpaperPath : Theme.background
} }
Rectangle { Rectangle {
@@ -18,6 +20,7 @@ Item {
height: parent.height * 1.5 height: parent.height * 1.5
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15)
rotation: 35 rotation: 35
visible: !isColorWallpaper
} }
Rectangle { Rectangle {
@@ -27,6 +30,7 @@ Item {
height: parent.height * 1.2 height: parent.height * 1.2
color: Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.12) color: Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.12)
rotation: 35 rotation: 35
visible: !isColorWallpaper
} }
Item { Item {
@@ -35,16 +39,18 @@ Item {
anchors.leftMargin: Theme.spacingXL * 2 anchors.leftMargin: Theme.spacingXL * 2
anchors.bottomMargin: Theme.spacingXL * 2 anchors.bottomMargin: Theme.spacingXL * 2
opacity: 0.25 opacity: 0.25
visible: !isColorWallpaper
StyledText { StyledText {
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
// ! TODO qmlfmt will brick this
text: ` text: `
` `
isMonospace: true isMonospace: true
font.pixelSize: Theme.fontSizeLarge * 1.2 font.pixelSize: Theme.fontSizeLarge * 1.2
color: Theme.primary color: Theme.primary

117
Widgets/DankColorPicker.qml Normal file
View File

@@ -0,0 +1,117 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
property string pickerTitle: "Choose Color"
property color selectedColor: Theme.primary
property bool isOpen: false
signal colorSelected(color selectedColor)
function open() {
customColorField.text = ""
isOpen = true
Qt.callLater(() => root.forceActiveFocus())
}
function close() {
isOpen = false
}
anchors.centerIn: parent
width: 320
height: 340
radius: Theme.cornerRadius
color: Theme.surfaceContainer
border.color: Theme.outlineMedium
border.width: 1
z: 1000
visible: isOpen
focus: isOpen
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
close()
event.accepted = true
}
}
DankActionButton {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: Theme.spacingS
buttonSize: 28
iconName: "close"
iconSize: 16
iconColor: Theme.surfaceText
onClicked: root.close()
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
StyledText {
text: pickerTitle
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
}
Grid {
columns: 8
spacing: 4
anchors.horizontalCenter: parent.horizontalCenter
property var colors: ["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b", "#000000", "#ffffff", "#ff1744", "#f50057", "#d500f9", "#651fff", "#3d5afe", "#2979ff", "#00b0ff", "#00e5ff", "#1de9b6", "#00e676", "#76ff03", "#c6ff00", "#ffff00", "#ffc400", "#ff9100", "#ff3d00", "#bf360c", "#424242", "#37474f"]
Repeater {
model: parent.colors
Rectangle {
width: 24
height: 24
color: modelData
radius: 4
border.color: Theme.outline
border.width: root.selectedColor == modelData ? 2 : 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedColor = modelData
root.colorSelected(modelData)
root.close()
}
}
}
}
}
StyledText {
text: "Custom Color:"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
DankTextField {
id: customColorField
width: parent.width
height: 40
placeholderText: "#ff0000"
text: ""
onAccepted: {
var hexColor = text.startsWith("#") ? text : "#" + text
if (/^#[0-9A-Fa-f]{6}$/.test(hexColor)) {
root.selectedColor = hexColor
root.colorSelected(hexColor)
root.close()
}
}
}
}
}