mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
fix(dock/taskbar): pinning specific apps no longer creates duplicate icons
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -505,7 +505,7 @@ PopupWindow {
|
||||
if (appId) {
|
||||
root.toggleAppPin(appId);
|
||||
}
|
||||
closeAndReset();
|
||||
Qt.callLater(() => closeAndReset());
|
||||
}
|
||||
|
||||
function handleClose(targetToplevel) {
|
||||
|
||||
@@ -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 = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user