fix(dock/taskbar): pinning specific apps no longer creates duplicate icons

This commit is contained in:
Lysec
2026-03-02 12:31:20 +01:00
parent bdfd8720dc
commit 1c4ee0dbc3
4 changed files with 135 additions and 7 deletions
+44 -2
View File
@@ -203,7 +203,38 @@ Item {
if (!appId || !pinnedApps || pinnedApps.length === 0)
return false;
const normalizedId = normalizeAppId(appId);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId);
// Direct match
if (pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId))
return true;
// Resolve via desktop entry lookup (handles StartupWMClass != .desktop filename)
const resolved = resolveToDesktopEntryId(appId);
if (resolved !== appId) {
const normalizedResolved = normalizeAppId(resolved);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedResolved);
}
return false;
}
// Desktop entry ID resolution cache (cleared when DesktopEntries change)
property var _desktopEntryIdCache: ({})
// Resolve a toplevel appId to its canonical .desktop entry ID via heuristic lookup.
function resolveToDesktopEntryId(appId) {
if (!appId)
return appId;
if (_desktopEntryIdCache.hasOwnProperty(appId))
return _desktopEntryIdCache[appId];
try {
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
const entry = DesktopEntries.heuristicLookup(appId);
if (entry && entry.id) {
_desktopEntryIdCache[appId] = entry.id;
return entry.id;
}
}
} catch (e) {}
_desktopEntryIdCache[appId] = appId;
return appId;
}
// Helper function to get app name from desktop entry
@@ -272,7 +303,14 @@ Item {
return false;
const pinnedApps = Settings.data.dock.pinnedApps || [];
const normalizedId = normalizeAppId(appId);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId);
if (pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId))
return true;
const resolved = resolveToDesktopEntryId(appId);
if (resolved !== appId) {
const normalizedResolved = normalizeAppId(resolved);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedResolved);
}
return false;
}
// Helper function to toggle app pin/unpin
@@ -331,6 +369,10 @@ Item {
"title": w.title || getAppNameFromDesktopEntry(w.appId)
});
processedAppIds.add(normalizeAppId(w.appId));
// Also track the resolved desktop entry ID so pinned app matching works
const resolvedId = resolveToDesktopEntryId(w.appId);
if (resolvedId !== w.appId)
processedAppIds.add(normalizeAppId(resolvedId));
}
}
} catch (e)
+46 -2
View File
@@ -67,6 +67,7 @@ Loader {
target: DesktopEntries.applications
function onValuesChanged() {
root.iconRevision++;
root._desktopEntryIdCache = {};
updateDockApps();
}
}
@@ -321,7 +322,40 @@ Loader {
if (!appId || !pinnedApps || pinnedApps.length === 0)
return false;
const normalizedId = normalizeAppId(appId);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId);
// Direct match
if (pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId))
return true;
// Resolve via desktop entry lookup (handles StartupWMClass != .desktop filename)
const resolved = resolveToDesktopEntryId(appId);
if (resolved !== appId) {
const normalizedResolved = normalizeAppId(resolved);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedResolved);
}
return false;
}
// Desktop entry ID resolution cache (cleared when DesktopEntries change)
property var _desktopEntryIdCache: ({})
// Resolve a toplevel appId to its canonical .desktop entry ID via heuristic lookup.
// This handles cases where the Wayland appId (e.g. "zen" from StartupWMClass)
// differs from the .desktop filename (e.g. "zen-browser-bin").
function resolveToDesktopEntryId(appId) {
if (!appId)
return appId;
if (_desktopEntryIdCache.hasOwnProperty(appId))
return _desktopEntryIdCache[appId];
try {
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
const entry = DesktopEntries.heuristicLookup(appId);
if (entry && entry.id) {
_desktopEntryIdCache[appId] = entry.id;
return entry.id;
}
}
} catch (e) {}
_desktopEntryIdCache[appId] = appId;
return appId;
}
// Helper function to get app name from desktop entry
@@ -497,7 +531,17 @@ Loader {
function pushPinned() {
pinnedApps.forEach(pinnedAppId => {
// Find all running instances of this pinned app using robust matching
const matchingToplevels = runningApps.filter(app => app && normalizeAppId(app.appId) === normalizeAppId(pinnedAppId));
// Also resolve toplevel appId via desktop entry lookup to handle
// StartupWMClass != .desktop filename (e.g. zen -> zen-browser-bin)
const normalizedPinned = normalizeAppId(pinnedAppId);
const matchingToplevels = runningApps.filter(app => {
if (!app)
return false;
if (normalizeAppId(app.appId) === normalizedPinned)
return true;
const resolved = resolveToDesktopEntryId(app.appId);
return resolved !== app.appId && normalizeAppId(resolved) === normalizedPinned;
});
if (matchingToplevels.length > 0) {
// Add all running instances as pinned-running
+1 -1
View File
@@ -505,7 +505,7 @@ PopupWindow {
if (appId) {
root.toggleAppPin(appId);
}
closeAndReset();
Qt.callLater(() => closeAndReset());
}
function handleClose(targetToplevel) {
+44 -2
View File
@@ -210,7 +210,38 @@ SmartPanel {
if (!appId || !pinnedApps || pinnedApps.length === 0)
return false;
const normalizedId = normalizeAppId(appId);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId);
// Direct match
if (pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedId))
return true;
// Resolve via desktop entry lookup (handles StartupWMClass != .desktop filename)
const resolved = resolveToDesktopEntryId(appId);
if (resolved !== appId) {
const normalizedResolved = normalizeAppId(resolved);
return pinnedApps.some(pinnedId => normalizeAppId(pinnedId) === normalizedResolved);
}
return false;
}
// Desktop entry ID resolution cache (cleared when DesktopEntries change)
property var _desktopEntryIdCache: ({})
// Resolve a toplevel appId to its canonical .desktop entry ID via heuristic lookup.
function resolveToDesktopEntryId(appId) {
if (!appId)
return appId;
if (_desktopEntryIdCache.hasOwnProperty(appId))
return _desktopEntryIdCache[appId];
try {
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
const entry = DesktopEntries.heuristicLookup(appId);
if (entry && entry.id) {
_desktopEntryIdCache[appId] = entry.id;
return entry.id;
}
}
} catch (e) {}
_desktopEntryIdCache[appId] = appId;
return appId;
}
// Helper function to get app name from desktop entry
@@ -386,7 +417,17 @@ SmartPanel {
function pushPinned() {
pinnedApps.forEach(pinnedAppId => {
// Find all running instances of this pinned app using robust matching
const matchingToplevels = runningApps.filter(app => app && normalizeAppId(app.appId) === normalizeAppId(pinnedAppId));
// Also resolve toplevel appId via desktop entry lookup to handle
// StartupWMClass != .desktop filename (e.g. zen -> zen-browser-bin)
const normalizedPinned = normalizeAppId(pinnedAppId);
const matchingToplevels = runningApps.filter(app => {
if (!app)
return false;
if (normalizeAppId(app.appId) === normalizedPinned)
return true;
const resolved = resolveToDesktopEntryId(app.appId);
return resolved !== app.appId && normalizeAppId(resolved) === normalizedPinned;
});
if (matchingToplevels.length > 0) {
// Add all running instances as pinned-running
@@ -487,6 +528,7 @@ SmartPanel {
target: DesktopEntries.applications
function onValuesChanged() {
root.iconRevision++;
root._desktopEntryIdCache = {};
}
}