From 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 Mon Sep 17 00:00:00 2001 From: AlexanderCurl Date: Sat, 18 Apr 2026 17:46:06 +0100 Subject: Replaced file structures for themes in the correct format --- .../utils.js | 346 +++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js (limited to 'raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js') diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js new file mode 100644 index 0000000..898b047 --- /dev/null +++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js @@ -0,0 +1,346 @@ +import Gdk from "gi://Gdk"; +import GdkPixbuf from "gi://GdkPixbuf"; +import Gio from "gi://Gio"; +import Gtk from "gi://Gtk"; +import St from "gi://St"; +import GLib from "gi://GLib"; + +let iconCacheMap = new Map(); +const MAX_CACHED_ITEMS = 1000; +const BATCH_SIZE_TO_DELETE = 50; +const DOMINANT_COLOR_ICON_SIZE = 64; + +function debugLog(msg) { + try { + let f = Gio.File.new_for_path('/tmp/color_log.txt'); + let out = f.append_to(Gio.FileCreateFlags.NONE, null); + out.write_all(new Date().toISOString() + " - " + msg + "\n", null); + out.close(null); + } catch (e) { } +} + +function findFallbackIconPath(iconName) { + if (!iconName) return null; + if (GLib.file_test(iconName, GLib.FileTest.EXISTS)) return iconName; + + const sizes = ['128x128', '256x256', '512x512', '64x64', 'scalable', '48x48']; + + let currentTheme = 'Adwaita'; + try { + const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); + if (settings.get_string('icon-theme')) { + currentTheme = settings.get_string('icon-theme'); + } + } catch (e) { + debugLog("Failed to get active icon theme setting: " + e); + } + + // Tegyük a hicolort az első helyre, a jelenleg aktív témát a másodikra, aztán a többi népszerű. + // A Set-tel kiszűrjük az esetleges ismétlődéseket. + const themes = [...new Set(['hicolor', currentTheme, 'locolor', 'Yaru', 'Pop', 'Adwaita', 'MoreWaita', 'Papirus', 'Tela'])]; + const bases = [ + GLib.get_home_dir() + '/.local/share/icons', + GLib.get_home_dir() + '/.local/share/flatpak/exports/share/icons', + '/var/lib/flatpak/exports/share/icons', + '/var/lib/snapd/desktop/icons', + '/usr/share/icons', + '/usr/local/share/icons' + ]; + const formats = ['.svg', '.png', '.xpm']; + + for (const base of bases) { + for (const theme of themes) { + for (const size of sizes) { + for (const format of formats) { + let path = `${base}/${theme}/${size}/apps/${iconName}${format}`; + if (GLib.file_test(path, GLib.FileTest.EXISTS)) return path; + } + } + } + } + + // Check pixmaps + for (const format of formats) { + let path = `/usr/share/pixmaps/${iconName}${format}`; + if (GLib.file_test(path, GLib.FileTest.EXISTS)) return path; + } + + return null; +} + +export class DominantColorExtractor { + constructor(app) { + this._app = app; + } + + _getIconPixBuf() { + debugLog("--- START Extracting for app: " + (this._app ? this._app.get_id() : "undefined") + " ---"); + if (!this._app) { + debugLog("Error: _app is null or undefined"); + return null; + } + + const gicon = this._app.get_icon(); + if (!gicon) { + debugLog("Fail: get_icon() returned null"); + return null; + } + + debugLog("Got gicon of type: " + gicon.constructor.name); + + // Try to load via Gio.FileIcon directly + if (gicon instanceof Gio.FileIcon) { + try { + debugLog("gicon is Gio.FileIcon. Getting file path..."); + const path = gicon.get_file().get_path(); + debugLog("File path: " + path); + if (path) { + return GdkPixbuf.Pixbuf.new_from_file(path); + } + } catch (e) { + debugLog("Exception in Gio.FileIcon lookup: " + e); + } + } + + // Try fallback manual lookup + if (gicon instanceof Gio.ThemedIcon) { + try { + debugLog("Attempting manual fallback search for ThemedIcon..."); + const names = gicon.get_names(); + for (const name of names) { + let path = findFallbackIconPath(name); + if (path) { + debugLog("Manual fallback found file: " + path); + return GdkPixbuf.Pixbuf.new_from_file(path); + } + } + } catch (e) { + debugLog("Exception in manual fallback: " + e); + } + } + + // Try St.TextureCache as a last resort + try { + debugLog("Attempting St.TextureCache lookup..."); + let textureCache = St.TextureCache.get_default(); + let themeContext = St.ThemeContext.get_for_stage(global.stage); + let iconSize = 64 * themeContext.scale_factor; + + let iconTexture = textureCache.load_gicon(null, gicon, iconSize); + if (iconTexture) { + debugLog("Found texture in cache, but cannot easily convert to Pixbuf in this context. Fallback to default."); + } + } catch (e) { + debugLog("St.TextureCache lookup failed: " + e); + } + + debugLog("--- FAIL: Exhausted all methods to get icon pixbuf ---"); + return null; + } + + _getColorPalette() { + debugLog("--- START _getColorPalette ---"); + const appId = this._app ? this._app.get_id() : null; + debugLog("App ID: " + appId); + + if (appId && iconCacheMap.has(appId)) { + debugLog("Returning cached color for: " + appId); + return iconCacheMap.get(appId); + } + + debugLog("Cache miss, calling _getIconPixBuf()"); + const pixBuf = this._getIconPixBuf(); + if (!pixBuf) { + debugLog("FAIL: pixBuf is null, cannot extract color."); + return null; + } + + try { + const width = pixBuf.get_width(); + const height = pixBuf.get_height(); + const rowstride = pixBuf.get_rowstride(); + const nChannels = pixBuf.get_n_channels(); + const hasAlpha = pixBuf.get_has_alpha(); + + debugLog(`PixBuf info: ${width}x${height}, rowstride: ${rowstride}, channels: ${nChannels}, alpha: ${hasAlpha}`); + + const pixels = pixBuf.get_pixels(); + if (!pixels) { + debugLog("FAIL: pixBuf.get_pixels() returned null or undefined"); + return null; + } + + let total = 0, rTotal = 0, gTotal = 0, bTotal = 0; + const step = Math.max(1, Math.floor(width / 32)); + debugLog("Calculated step for sampling: " + step); + + let sampledCount = 0; + for (let y = 0; y < height; y += step) { + for (let x = 0; x < width; x += step) { + const offset = y * rowstride + x * nChannels; + const r = pixels[offset]; + const g = pixels[offset + 1]; + const b = pixels[offset + 2]; + const a = hasAlpha ? pixels[offset + 3] : 255; + + if (a < 128) continue; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const saturation = (max - min) / (max || 1); + const relevance = a * (saturation * saturation + 0.1); + + rTotal += r * relevance; + gTotal += g * relevance; + bTotal += b * relevance; + total += relevance; + sampledCount++; + } + } + + debugLog(`Sampling complete. Sampled pixels: ${sampledCount}. Total accumulator: ${total}`); + + if (total === 0) { + debugLog("FAIL: Valid pixel total is 0. Returning null."); + return null; + } + + let r = rTotal / total; + let g = gTotal / total; + let b = bTotal / total; + debugLog(`Averaged RGB: Math.round(${r}), Math.round(${g}), Math.round(${b})`); + + let hsv = ColorUtils.RGBtoHSV(r, g, b); + debugLog(`HSV before normalize: h:${hsv.h.toFixed(2)}, s:${hsv.s.toFixed(2)}, v:${hsv.v.toFixed(2)}`); + + if (hsv.s < 0.2) hsv.s = 0.35; + hsv.v = Math.max(0.6, Math.min(hsv.v, 0.9)); + + debugLog(`HSV after normalize: h:${hsv.h.toFixed(2)}, s:${hsv.s.toFixed(2)}, v:${hsv.v.toFixed(2)}`); + + const rgb = ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v); + + const backgroundColor = { + lighter: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.5), + original: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0), + darker: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, -0.5), + baseRgb: rgb + }; + + debugLog(`Generated Palette: original=${backgroundColor.original}, lighter=${backgroundColor.lighter}, darker=${backgroundColor.darker}`); + + if (iconCacheMap.size >= MAX_CACHED_ITEMS) { + debugLog("Cache full, clearing batch."); + const keys = Array.from(iconCacheMap.keys()); + for (let i = 0; i < BATCH_SIZE_TO_DELETE; i++) { + iconCacheMap.delete(keys[i]); + } + } + + if (appId) { + iconCacheMap.set(appId, backgroundColor); + debugLog("Added to cache for appId: " + appId); + } + + debugLog("--- SUCCESS: Color extraction finished ---"); + return backgroundColor; + + } catch (e) { + debugLog("Exception during pixel processing: " + e); + return null; + } + } +} + +export class ColorUtils { + static ColorLuminance(r, g, b, dlum) { + let rgbString = '#'; + rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), 2); + rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), 2); + rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), 2); + return rgbString; + } + + static _decimalToHex(d, padding) { + let hex = d.toString(16); + while (hex.length < padding) + hex = '0' + hex; + return hex; + } + + static _hexToRgb(h) { + return { + r: parseInt(h.substr(1, 2), 16), + g: parseInt(h.substr(3, 2), 16), + b: parseInt(h.substr(5, 2), 16) + } + } + + static HSVtoRGB(h, s, v) { + if (arguments.length === 1) { + s = h.s; + v = h.v; + h = h.h; + } + + let r, g, b; + let c = v * s; + let h1 = h * 6; + let x = c * (1 - Math.abs(h1 % 2 - 1)); + let m = v - c; + + if (h1 <= 1) + r = c + m, g = x + m, b = m; + else if (h1 <= 2) + r = x + m, g = c + m, b = m; + else if (h1 <= 3) + r = m, g = c + m, b = x + m; + else if (h1 <= 4) + r = m, g = x + m, b = c + m; + else if (h1 <= 5) + r = x + m, g = m, b = c + m; + else + r = c + m, g = m, b = x + m; + + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255) + }; + } + + static RGBtoHSV(r, g, b) { + if (arguments.length === 1) { + r = r.r; + g = r.g; + b = r.b; + } + + let h, s, v; + let M = Math.max(r, g, b); + let m = Math.min(r, g, b); + let c = M - m; + + if (c == 0) + h = 0; + else if (M == r) + h = ((g - b) / c) % 6; + else if (M == g) + h = (b - r) / c + 2; + else + h = (r - g) / c + 4; + + h = h / 6; + v = M / 255; + if (M !== 0) + s = c / M; + else + s = 0; + + return { + h: h, + s: s, + v: v + }; + } +} -- cgit v1.3