Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
597 changes: 597 additions & 0 deletions .temp-icon-resolver/ActiveWindow-with-resolver.qml

Large diffs are not rendered by default.

784 changes: 784 additions & 0 deletions .temp-icon-resolver/Dock-with-resolver.qml

Large diffs are not rendered by default.

1,522 changes: 1,522 additions & 0 deletions .temp-icon-resolver/Launcher-with-resolver.qml

Large diffs are not rendered by default.

197 changes: 159 additions & 38 deletions Modules/Bar/Widgets/ActiveWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Quickshell.Widgets
import qs.Commons
import qs.Modules.Bar.Extras
import qs.Services.Compositor
import qs.Services.Icons
import qs.Services.UI
import qs.Widgets

Expand Down Expand Up @@ -117,44 +118,28 @@ Item {

function getAppIcon() {
try {
// Try CompositorService first
const focusedWindow = CompositorService.getFocusedWindow();
let appId = null;

if (focusedWindow && focusedWindow.appId) {
try {
const idValue = focusedWindow.appId;
const normalizedId = (typeof idValue === 'string') ? idValue : String(idValue);
const iconResult = ThemeIcons.iconForAppId(normalizedId.toLowerCase());
if (iconResult && iconResult !== "") {
return iconResult;
}
} catch (iconError) {
Logger.w("ActiveWindow", "Error getting icon from CompositorService:", iconError);
}
}

if (CompositorService.isHyprland) {
// Fallback to ToplevelManager
if (ToplevelManager && ToplevelManager.activeToplevel) {
try {
const activeToplevel = ToplevelManager.activeToplevel;
if (activeToplevel.appId) {
const idValue2 = activeToplevel.appId;
const normalizedId2 = (typeof idValue2 === 'string') ? idValue2 : String(idValue2);
const iconResult2 = ThemeIcons.iconForAppId(normalizedId2.toLowerCase());
if (iconResult2 && iconResult2 !== "") {
return iconResult2;
}
}
} catch (fallbackError) {
Logger.w("ActiveWindow", "Error getting icon from ToplevelManager:", fallbackError);
}
const idValue = focusedWindow.appId;
appId = (typeof idValue === 'string') ? idValue : String(idValue);
} else if (CompositorService.isHyprland && ToplevelManager && ToplevelManager.activeToplevel) {
const activeToplevel = ToplevelManager.activeToplevel;
if (activeToplevel.appId) {
const idValue2 = activeToplevel.appId;
appId = (typeof idValue2 === 'string') ? idValue2 : String(idValue2);
}
}

return ThemeIcons.iconFromName(fallbackIcon);

const iconName = appId ? AppSearch.guessIcon(appId.toLowerCase()) : AppSearch.guessIcon(fallbackIcon);
Logger.d("ActiveWindow", "getAppIcon returning:", iconName)
return iconName;
} catch (e) {
Logger.w("ActiveWindow", "Error in getAppIcon:", e);
return ThemeIcons.iconFromName(fallbackIcon);
const iconName = AppSearch.guessIcon(fallbackIcon);
Logger.d("ActiveWindow", "getAppIcon returning (error fallback):", iconName)
return iconName;
}
}

Expand Down Expand Up @@ -234,10 +219,49 @@ Item {
IconImage {
id: windowIcon
anchors.fill: parent
source: getAppIcon()
asynchronous: true
smooth: true
visible: source !== ""

Component.onCompleted: {
var iconName = getAppIcon();
// Resolve with fallback (caches ThemeIcons results for performance)
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
}
// If path is empty, leave blank (resolver not ready yet)
});
}

// Refresh icon when IconResolver becomes ready (in case it wasn't ready on first load)
Connections {
target: IconResolver
function onReadyChanged() {
if (IconResolver.ready && (windowIcon.source === "" || !windowIcon.source)) {
// If icon is still empty when resolver becomes ready, try again
var iconName = getAppIcon();
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
}
});
}
}
function onResolverRestarted() {
// Clear icon source and re-resolve when theme changes
windowIcon.source = "";
var iconName = getAppIcon();
// Wait for resolver to become ready, then resolve
Qt.callLater(function() {
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
}
});
});
}
}

// Apply dock shader to active window icon (always themed)
layer.enabled: widgetSettings.colorizeIcons !== false
Expand Down Expand Up @@ -413,10 +437,49 @@ Item {
IconImage {
id: windowIconVertical
anchors.fill: parent
source: getAppIcon()
asynchronous: true
smooth: true
visible: source !== ""

Component.onCompleted: {
var iconName = getAppIcon();
// Resolve with fallback (caches ThemeIcons results for performance)
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIconVertical.source = path;
}
// If path is empty, leave blank (resolver not ready yet)
});
}

// Refresh icon when IconResolver becomes ready (in case it wasn't ready on first load)
Connections {
target: IconResolver
function onReadyChanged() {
if (IconResolver.ready && (windowIconVertical.source === "" || !windowIconVertical.source)) {
// If icon is still empty when resolver becomes ready, try again
var iconName = getAppIcon();
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIconVertical.source = path;
}
});
}
}
function onResolverRestarted() {
// Clear icon source and re-resolve when theme changes
windowIconVertical.source = "";
var iconName = getAppIcon();
// Wait for resolver to become ready, then resolve
Qt.callLater(function() {
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIconVertical.source = path;
}
});
});
}
}

// Apply dock shader to active window icon (always themed)
layer.enabled: widgetSettings.colorizeIcons !== false
Expand Down Expand Up @@ -462,16 +525,74 @@ Item {
target: CompositorService
function onActiveWindowChanged() {
try {
windowIcon.source = Qt.binding(getAppIcon);
windowIconVertical.source = Qt.binding(getAppIcon);
// Clear icons immediately to prevent showing stale icons during resolver restart
windowIcon.source = "";
windowIconVertical.source = "";

const iconName = getAppIcon();

// If resolver is restarting, wait for it to be ready before resolving
// This prevents mismatched icons when switching windows during theme changes
if (IconResolver.isRestarting || !IconResolver.ready) {
// Wait for resolver to be ready, then resolve
var checkReady = function() {
if (IconResolver.ready && !IconResolver.isRestarting) {
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
windowIconVertical.source = path;
}
});
} else {
// Check again in a moment
Qt.callLater(checkReady);
}
};
Qt.callLater(checkReady);
} else {
// Resolver is ready, resolve immediately
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
windowIconVertical.source = path;
}
});
}
} catch (e) {
Logger.w("ActiveWindow", "Error in onActiveWindowChanged:", e);
}
}
function onWindowListChanged() {
try {
windowIcon.source = Qt.binding(getAppIcon);
windowIconVertical.source = Qt.binding(getAppIcon);
// Clear icons immediately to prevent showing stale icons during resolver restart
windowIcon.source = "";
windowIconVertical.source = "";

const iconName = getAppIcon();

// If resolver is restarting, wait for it to be ready before resolving
if (IconResolver.isRestarting || !IconResolver.ready) {
var checkReady = function() {
if (IconResolver.ready && !IconResolver.isRestarting) {
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
windowIconVertical.source = path;
}
});
} else {
Qt.callLater(checkReady);
}
};
Qt.callLater(checkReady);
} else {
AppSearch.resolveIconWithFallback(iconName, function(path) {
if (path && path.length > 0) {
windowIcon.source = path;
windowIconVertical.source = path;
}
});
}
} catch (e) {
Logger.w("ActiveWindow", "Error in onWindowListChanged:", e);
}
Expand Down
Loading