1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-02 10:32:07 -04:00

Compare commits

...

6 Commits

Author SHA1 Message Date
purian23
971a511edb fix(notifications): Apply appIdSubs to iconFrImage fallback path
- Consistent with the
appIcon PR changes in #1880.
2026-03-01 17:37:21 -05:00
odt
0f8e0bc2b4 refactor(icons): centralize icon resolution into Paths.resolveIconPath/resolveIconUrl (#1880)
Supersedes #1878. Rather than duplicating the moddedAppId + file path
substitution pattern inline across 8 files, this introduces two
centralized functions in Paths.qml:

- resolveIconPath(iconName): for Quickshell.iconPath() callsites,
  with DesktopService.resolveIconPath() fallback
- resolveIconUrl(iconName): for image://icon/ URL callsites

All consumer files now use one-line calls. When no substitutions are
configured, moddedAppId() returns the original name unchanged (zero
cost), so this has no impact on users who don't use the feature.

Affected components:
- AppIconRenderer (8 lines → 1)
- NotificationCard, NotificationPopup, HistoryNotificationCard
- DockContextMenu, AppsDockContextMenu
- LauncherContent, LauncherTab (×3)

Co-authored-by: odtgit <odtgit@taliops.com>
2026-03-01 17:31:51 -05:00
supposede
537c44e354 Update toolbar button styles with primary color (#1879) 2026-03-01 16:51:40 -05:00
bbedward
db53a9a719 i18n: decouple time and language locale
fixes #1876
2026-03-01 15:17:34 -05:00
odt
f4a10de790 fix(icons): apply file path substitutions in launcher icon resolution (#1877)
Follow-up to #1867. The launcher's AppIconRenderer used its own
Quickshell.iconPath() call without going through appIdSubstitutions,
so PWA icons configured via regex file path rules were not resolved
in the app launcher.

Co-authored-by: odtgit <odtgit@taliops.com>
2026-03-01 15:03:28 -05:00
bbedward
8c9fe84d02 wallpaper: bump render settle timer 2026-03-01 10:26:46 -05:00
16 changed files with 411 additions and 137 deletions

View File

@@ -1,5 +1,6 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Qt.labs.folderlistmodel
import Quickshell
@@ -24,7 +25,9 @@ Singleton {
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
readonly property alias folder: dir.folder
property var presentLocales: ({ "en": Qt.locale("en") })
property var presentLocales: ({
"en": Qt.locale("en")
})
property var translations: ({})
property bool translationsLoaded: false
@@ -65,7 +68,9 @@ Singleton {
}
function locale() {
return presentLocales[_resolvedLocale] ?? presentLocales["en"];
if (SessionData.timeLocale)
return Qt.locale(SessionData.timeLocale);
return Qt.locale();
}
function _loadPresentLocales() {
@@ -84,7 +89,8 @@ Singleton {
function _pickTranslation() {
for (let i = 0; i < _candidates.length; i++) {
const cand = _candidates[i];
if (presentLocales[cand] === undefined) continue;
if (presentLocales[cand] === undefined)
continue;
_resolvedLocale = cand;
useLocale(cand, cand.startsWith("en") ? "" : translationsFolder + "/" + cand + ".json");
return;

View File

@@ -71,19 +71,40 @@ Singleton {
return appId;
}
function getAppIcon(appId: string, desktopEntry: var): string {
if (appId === "org.quickshell") {
return Qt.resolvedUrl("../assets/danklogo.svg");
}
const moddedId = moddedAppId(appId);
if (moddedId !== appId) {
function resolveIconPath(iconName: string): string {
if (!iconName) return "";
const moddedId = moddedAppId(iconName);
if (moddedId !== iconName) {
if (moddedId.startsWith("~") || moddedId.startsWith("/"))
return toFileUrl(expandTilde(moddedId));
if (moddedId.startsWith("file://"))
return moddedId;
return Quickshell.iconPath(moddedId, true);
}
return Quickshell.iconPath(iconName, true) || DesktopService.resolveIconPath(iconName);
}
function resolveIconUrl(iconName: string): string {
if (!iconName) return "";
const moddedId = moddedAppId(iconName);
if (moddedId !== iconName) {
if (moddedId.startsWith("~") || moddedId.startsWith("/"))
return toFileUrl(expandTilde(moddedId));
if (moddedId.startsWith("file://"))
return moddedId;
return "image://icon/" + moddedId;
}
return "image://icon/" + iconName;
}
function getAppIcon(appId: string, desktopEntry: var): string {
if (appId === "org.quickshell") {
return Qt.resolvedUrl("../assets/danklogo.svg");
}
const moddedId = moddedAppId(appId);
if (moddedId !== appId)
return resolveIconPath(appId);
if (desktopEntry && desktopEntry.icon) {
return Quickshell.iconPath(desktopEntry.icon, true);

View File

@@ -129,6 +129,7 @@ Singleton {
property var hiddenInputDeviceNames: []
property string locale: ""
property string timeLocale: ""
property string launcherLastMode: "all"
property string appDrawerLastMode: "apps"

View File

@@ -80,6 +80,7 @@ var SPEC = {
hiddenInputDeviceNames: { def: [] },
locale: { def: "", onChange: "updateLocale" },
timeLocale: { def: "" },
launcherLastMode: { def: "all" },
appDrawerLastMode: { def: "apps" },

View File

@@ -789,7 +789,7 @@ FocusScope {
Image {
width: 40
height: 40
source: editingApp?.icon ? "image://icon/" + editingApp.icon : "image://icon/application-x-executable"
source: Paths.resolveIconUrl(editingApp?.icon || "application-x-executable")
sourceSize.width: 40
sourceSize.height: 40
fillMode: Image.PreserveAspectFit

View File

@@ -273,7 +273,7 @@ PanelWindow {
IconImage {
anchors.fill: parent
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
source: modelData.icon ? Paths.resolveIconPath(modelData.icon) : ""
smooth: true
asynchronous: true
visible: status === Image.Ready

View File

@@ -329,7 +329,7 @@ PanelWindow {
IconImage {
anchors.fill: parent
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : ""
source: modelData.icon ? Paths.resolveIconPath(modelData.icon) : ""
smooth: true
asynchronous: true
visible: status === Image.Ready

View File

@@ -137,12 +137,12 @@ Rectangle {
return "";
const appIcon = historyItem.appIcon;
if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : "";
return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return "";
return Quickshell.iconPath(appIcon, true);
return Paths.resolveIconPath(appIcon);
}
hasImage: hasNotificationImage

View File

@@ -215,12 +215,12 @@ Rectangle {
return "";
const appIcon = notificationGroup?.latestNotification?.appIcon;
if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : "";
return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return "";
return Quickshell.iconPath(appIcon, true);
return Paths.resolveIconPath(appIcon);
}
hasImage: hasNotificationImage
@@ -552,12 +552,12 @@ Rectangle {
return "";
const appIcon = modelData?.appIcon;
if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : "";
return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return "";
return Quickshell.iconPath(appIcon, true);
return Paths.resolveIconPath(appIcon);
}
fallbackIcon: {

View File

@@ -519,12 +519,12 @@ PanelWindow {
return "";
const appIcon = notificationData.appIcon;
if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : "";
return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return "";
return Quickshell.iconPath(appIcon, true);
return Paths.resolveIconPath(appIcon);
}
hasImage: hasNotificationImage

View File

@@ -897,7 +897,7 @@ Item {
Image {
width: 24
height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24
sourceSize.height: 24
fillMode: Image.PreserveAspectFit
@@ -1008,7 +1008,7 @@ Item {
Image {
width: 24
height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24
sourceSize.height: 24
fillMode: Image.PreserveAspectFit
@@ -1154,7 +1154,7 @@ Item {
Image {
width: 24
height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24
sourceSize.height: 24
fillMode: Image.PreserveAspectFit

View File

@@ -1,6 +1,5 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
@@ -9,17 +8,25 @@ Item {
readonly property string _systemDefaultLabel: I18n.tr("System Default")
function capitalizeNativeLanguageName(localeCode) {
if (I18n.presentLocales[localeCode] == undefined) {
function _localeDisplayName(localeCode) {
if (!I18n.presentLocales[localeCode])
return;
}
const nativeName = I18n.presentLocales[localeCode].nativeLanguageName;
return nativeName[0].toUpperCase() + nativeName.slice(1);
}
function _displayValue() {
if (!SessionData.locale) return _systemDefaultLabel;
return capitalizeNativeLanguageName(SessionData.locale);
function _allLocaleOptions() {
return [_systemDefaultLabel].concat(Object.keys(I18n.presentLocales).map(_localeDisplayName));
}
function _codeForDisplayName(displayName) {
if (displayName === _systemDefaultLabel)
return "";
for (const code of Object.keys(I18n.presentLocales)) {
if (_localeDisplayName(code) === displayName)
return code;
}
return "";
}
DankFlickable {
@@ -48,24 +55,34 @@ Item {
settingKey: "locale"
text: I18n.tr("Current Locale")
description: I18n.tr("Change the locale used by the DMS interface.")
options: [localeTab._systemDefaultLabel].concat(Object.keys(I18n.presentLocales).map(localeTab.capitalizeNativeLanguageName))
options: localeTab._allLocaleOptions()
enableFuzzySearch: true
Component.onCompleted: {
currentValue = localeTab._displayValue();
currentValue = SessionData.locale ? localeTab._localeDisplayName(SessionData.locale) : localeTab._systemDefaultLabel;
}
onValueChanged: value => {
if (value === localeTab._systemDefaultLabel) {
SessionData.set("locale", "");
return;
}
for (let code of Object.keys(I18n.presentLocales)) {
if (localeTab.capitalizeNativeLanguageName(code) === value) {
SessionData.set("locale", code);
return;
}
}
SessionData.set("locale", localeTab._codeForDisplayName(value));
}
}
SettingsDropdownRow {
id: timeLocaleDropdown
tab: "locale"
tags: ["locale", "time", "date", "format", "region"]
settingKey: "timeLocale"
text: I18n.tr("Time & Date Locale")
description: I18n.tr("Change the locale used for date and time formatting, independent of the interface language.")
options: localeTab._allLocaleOptions()
enableFuzzySearch: true
Component.onCompleted: {
currentValue = SessionData.timeLocale ? localeTab._localeDisplayName(SessionData.timeLocale) : localeTab._systemDefaultLabel;
}
onValueChanged: value => {
SessionData.set("timeLocale", localeTab._codeForDisplayName(value));
}
}
}

View File

@@ -135,7 +135,7 @@ Variants {
Timer {
id: renderSettleTimer
interval: 100
interval: 1000
onTriggered: root._renderSettling = false
}

View File

@@ -49,7 +49,7 @@ Item {
readonly property string iconPath: {
if (hasSpecialPrefix || !iconValue)
return "";
return Quickshell.iconPath(iconValue, true) || DesktopService.resolveIconPath(iconValue);
return Paths.resolveIconPath(iconValue);
}
visible: iconValue !== undefined && iconValue !== ""

View File

@@ -92,3 +92,21 @@ toolbar .toolbarbutton-1 {
#zen-appcontent-navbar-container {
background-color: {{colors.background.default.hex}} !important;
}
#PanelUI-menu-button .toolbarbutton-icon,
#downloads-button .toolbarbutton-icon,
#unified-extensions-button .toolbarbutton-icon {
fill: {{colors.primary.default.hex}} !important;
color: {{colors.primary.default.hex}} !important;
}
#PanelUI-menu-button .toolbarbutton-badge-stack,
#downloads-button .toolbarbutton-badge-stack,
#unified-extensions-button .toolbarbutton-badge-stack {
fill: {{colors.primary.default.hex}} !important;
color: {{colors.primary.default.hex}} !important;
}
toolbar .toolbarbutton-1 > .toolbarbutton-icon {
fill: {{colors.primary.default.hex}} !important;
}

View File

@@ -733,6 +733,26 @@
],
"icon": "toolbar"
},
{
"section": "barShadowDirectionSource",
"label": "Direction Source",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"bar",
"choose",
"dank",
"direction",
"panel",
"resolves",
"shadow",
"source",
"statusbar",
"taskbar",
"topbar"
],
"description": "Choose how this bar resolves shadow direction"
},
{
"section": "barDisplay",
"label": "Display Assignment",
@@ -752,6 +772,25 @@
],
"icon": "display_settings"
},
{
"section": "barShadowDirectionManual",
"label": "Manual Direction",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"bar",
"dank",
"direction",
"fixed",
"manual",
"panel",
"shadow",
"statusbar",
"taskbar",
"topbar"
],
"description": "Use a fixed shadow direction for this bar"
},
{
"section": "barPosition",
"label": "Position",
@@ -769,18 +808,23 @@
},
{
"section": "barShadow",
"label": "Shadow",
"label": "Shadow Override",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"bar",
"dank",
"global",
"override",
"panel",
"settings",
"shadow",
"statusbar",
"taskbar",
"topbar"
],
"icon": "layers"
"icon": "layers",
"description": "Override the global shadow with per-bar settings"
},
{
"section": "barSpacing",
@@ -2252,6 +2296,33 @@
],
"icon": "schedule"
},
{
"section": "barElevationEnabled",
"label": "Bar Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"bar",
"bars",
"colors",
"elevation",
"look",
"m3",
"navigation",
"panel",
"panels",
"scheme",
"shadow",
"shadows",
"statusbar",
"style",
"taskbar",
"theme",
"topbar"
],
"description": "Shadow elevation on bars and panels"
},
{
"section": "niriLayoutBorderSize",
"label": "Border Size",
@@ -2416,74 +2487,6 @@
],
"description": "0 = square corners"
},
{
"section": "m3ElevationEnabled",
"label": "M3 Elevation & Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"elevation",
"lift",
"material",
"m3",
"shadow",
"shadows",
"theme"
],
"description": "Material Design 3 shadows and elevation on modals, popouts, and dialogs"
},
{
"section": "modalElevationEnabled",
"label": "Modal Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"dialog",
"elevation",
"m3",
"material",
"modal",
"shadow",
"shadows"
],
"description": "Shadow elevation on modals and dialogs"
},
{
"section": "popoutElevationEnabled",
"label": "Popout Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"dropdown",
"elevation",
"m3",
"material",
"osd",
"popout",
"popup",
"shadow",
"shadows"
],
"description": "Shadow elevation on popouts, OSDs, and dropdowns"
},
{
"section": "barElevationEnabled",
"label": "Bar Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"bar",
"elevation",
"m3",
"material",
"navigation",
"panel",
"shadow",
"shadows"
],
"description": "Shadow elevation on bars and panels"
},
{
"section": "cursorSize",
"label": "Cursor Size",
@@ -2758,6 +2761,32 @@
"tint"
]
},
{
"section": "m3ElevationLightDirection",
"label": "Light Direction",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"advanced",
"appearance",
"cast",
"colors",
"controls",
"day",
"direction",
"elevation",
"layers",
"light",
"light mode",
"look",
"m3",
"scheme",
"shadow",
"style",
"theme"
],
"description": "Controls shadow cast direction for elevation layers"
},
{
"section": "isLightMode",
"label": "Light Mode",
@@ -2898,6 +2927,29 @@
"icon": "layers",
"description": "Show darkened overlay behind modal dialogs"
},
{
"section": "modalElevationEnabled",
"label": "Modal Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"colors",
"dialog",
"dialogs",
"elevation",
"look",
"m3",
"modal",
"modals",
"scheme",
"shadow",
"shadows",
"style",
"theme"
],
"description": "Shadow elevation on modals and dialogs"
},
{
"section": "niriLayout",
"label": "Niri Layout Overrides",
@@ -3158,6 +3210,32 @@
],
"description": "Use custom gaps instead of bar spacing"
},
{
"section": "popoutElevationEnabled",
"label": "Popout Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"colors",
"dropdown",
"dropdowns",
"elevation",
"look",
"m3",
"osd",
"osds",
"popout",
"popouts",
"popup",
"scheme",
"shadow",
"shadows",
"style",
"theme"
],
"description": "Shadow elevation on popouts, OSDs, and dropdowns"
},
{
"section": "popupTransparency",
"label": "Popup Transparency",
@@ -3222,6 +3300,105 @@
"user"
]
},
{
"section": "m3ElevationColorMode",
"label": "Shadow Color",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"applied",
"base",
"color",
"colors",
"colour",
"elevation",
"hue",
"look",
"m3",
"scheme",
"shadow",
"shadows",
"style",
"theme",
"tint"
],
"description": "Base color for shadows (opacity is applied automatically)"
},
{
"section": "m3ElevationIntensity",
"label": "Shadow Intensity",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"base",
"blur",
"colors",
"controls",
"elevation",
"intensity",
"look",
"m3",
"offset",
"radius",
"scheme",
"shadow",
"shadows",
"style",
"theme"
],
"description": "Controls the base blur radius and offset of shadows"
},
{
"section": "m3ElevationOpacity",
"label": "Shadow Opacity",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"alpha",
"appearance",
"colors",
"controls",
"elevation",
"look",
"m3",
"opacity",
"scheme",
"shadow",
"style",
"theme",
"translucent",
"transparency",
"transparent"
],
"description": "Controls the transparency of the shadow"
},
{
"section": "m3ElevationEnabled",
"label": "Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"colors",
"dialogs",
"elevation",
"inspired",
"lift",
"look",
"m3",
"material",
"modals",
"popouts",
"scheme",
"shadow",
"shadows",
"style",
"theme"
],
"description": "Material inspired shadows and elevation on modals, popouts, and dialogs"
},
{
"section": "syncModeWithPortal",
"label": "Sync Mode with Portal",
@@ -4038,27 +4215,6 @@
],
"description": "Automatically lock the screen when DMS starts"
},
{
"section": "lockBeforeSuspend",
"label": "Lock before suspend",
"tabIndex": 11,
"category": "Lock Screen",
"keywords": [
"automatic",
"automatically",
"before",
"lock",
"login",
"password",
"prepares",
"screen",
"security",
"sleep",
"suspend",
"system"
],
"description": "Automatically lock the screen when the system prepares to suspend"
},
{
"section": "lockScreenNotificationMode",
"label": "Notification Display",
@@ -5358,20 +5514,33 @@
"keywords": [
"alert",
"alerts",
"appearance",
"colors",
"colour",
"colours",
"drop",
"elevation",
"enabled",
"hue",
"look",
"messages",
"notif",
"notification",
"notifications",
"palette",
"popup",
"popups",
"radius",
"requires",
"rounded",
"shadow",
"show",
"style",
"theme",
"tint",
"toast"
],
"description": "Show drop shadow on notification popups"
"description": "Show drop shadow on notification popups. Requires M3 Elevation to be enabled in Theme & Colors."
},
{
"section": "notificationPopupPrivacyMode",
@@ -6023,6 +6192,27 @@
"icon": "schedule",
"description": "Gradually fade the screen before locking with a configurable grace period"
},
{
"section": "lockBeforeSuspend",
"label": "Lock before suspend",
"tabIndex": 21,
"category": "Power & Sleep",
"keywords": [
"automatically",
"before",
"energy",
"lock",
"power",
"prepares",
"screen",
"security",
"shutdown",
"sleep",
"suspend",
"system"
],
"description": "Automatically lock the screen when the system prepares to suspend"
},
{
"section": "fadeToLockGracePeriod",
"label": "Lock fade grace period",
@@ -6618,5 +6808,25 @@
],
"icon": "language",
"description": "Change the locale used by the DMS interface."
},
{
"section": "timeLocale",
"label": "Time & Date Locale",
"tabIndex": 30,
"category": "Locale",
"keywords": [
"change",
"country",
"date",
"format",
"formatting",
"independent",
"interface",
"language",
"locale",
"region",
"time"
],
"description": "Change the locale used for date and time formatting, independent of the interface language."
}
]