mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 04:09:15 -04:00
plugins: add support for composite plugins
- single plugin can register multiple types - e.g. daemon, bar widget, desktop widget
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import Qt.labs.folderlistmodel
|
||||
import Quickshell
|
||||
@@ -202,8 +201,51 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var pluginSurfaceKeys: ["widget", "desktop", "daemon", "launcher"]
|
||||
|
||||
function _stripDotSlash(p) {
|
||||
return p.startsWith("./") ? p.slice(2) : p;
|
||||
}
|
||||
|
||||
function _deriveLegacySurface(type, capabilities) {
|
||||
if (type === "daemon")
|
||||
return "daemon";
|
||||
if (type === "launcher" || (capabilities && capabilities.includes("launcher")))
|
||||
return "launcher";
|
||||
if (type === "desktop")
|
||||
return "desktop";
|
||||
return "widget";
|
||||
}
|
||||
|
||||
function _resolveComponentPaths(manifest, dir) {
|
||||
const paths = {};
|
||||
if (manifest.components && typeof manifest.components === "object") {
|
||||
for (const surface in manifest.components) {
|
||||
if (!pluginSurfaceKeys.includes(surface)) {
|
||||
log.warn("unknown plugin surface", surface, "in", dir);
|
||||
continue;
|
||||
}
|
||||
const rel = manifest.components[surface];
|
||||
if (!rel)
|
||||
continue;
|
||||
paths[surface] = dir + "/" + _stripDotSlash(rel);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
if (manifest.component) {
|
||||
const surface = _deriveLegacySurface(manifest.type, manifest.capabilities);
|
||||
paths[surface] = dir + "/" + _stripDotSlash(manifest.component);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
function pluginHasSurface(pluginId, surface) {
|
||||
const plugin = availablePlugins[pluginId];
|
||||
return !!(plugin && plugin.surfaces && plugin.surfaces.includes(surface));
|
||||
}
|
||||
|
||||
function _onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs) {
|
||||
if (!manifest || !manifest.id || !manifest.name || !manifest.component) {
|
||||
if (!manifest || !manifest.id || !manifest.name || (!manifest.component && !manifest.components)) {
|
||||
log.error("invalid manifest fields:", absPath);
|
||||
knownManifests[absPath] = {
|
||||
mtime: mtimeEpochMs,
|
||||
@@ -214,13 +256,22 @@ Singleton {
|
||||
}
|
||||
|
||||
const dir = absPath.substring(0, absPath.lastIndexOf('/'));
|
||||
let comp = manifest.component;
|
||||
if (comp.startsWith("./"))
|
||||
comp = comp.slice(2);
|
||||
let settings = manifest.settings;
|
||||
if (settings && settings.startsWith("./"))
|
||||
settings = settings.slice(2);
|
||||
|
||||
const componentPaths = _resolveComponentPaths(manifest, dir);
|
||||
const surfaces = Object.keys(componentPaths);
|
||||
if (surfaces.length === 0) {
|
||||
log.error("no valid component surfaces in manifest:", absPath);
|
||||
knownManifests[absPath] = {
|
||||
mtime: mtimeEpochMs,
|
||||
source: sourceTag,
|
||||
bad: true
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const info = {};
|
||||
for (const k in manifest)
|
||||
info[k] = manifest[k];
|
||||
@@ -236,10 +287,12 @@ Singleton {
|
||||
|
||||
info.manifestPath = absPath;
|
||||
info.pluginDirectory = dir;
|
||||
info.componentPath = dir + "/" + comp;
|
||||
info.componentPaths = componentPaths;
|
||||
info.surfaces = surfaces;
|
||||
info.componentPath = componentPaths.widget || componentPaths[surfaces[0]];
|
||||
info.settingsPath = settings ? (dir + "/" + settings) : null;
|
||||
info.loaded = isPluginLoaded(manifest.id);
|
||||
info.type = manifest.type || "widget";
|
||||
info.type = manifest.type || (manifest.components ? "composite" : "widget");
|
||||
info.source = sourceTag;
|
||||
info.requires_dms = manifest.requires_dms || null;
|
||||
|
||||
@@ -260,7 +313,8 @@ Singleton {
|
||||
};
|
||||
_updateAvailablePluginsList();
|
||||
pluginListUpdated();
|
||||
const enabled = info.type === "desktop" || SettingsData.getPluginSetting(manifest.id, "enabled", false);
|
||||
const isPureDesktop = surfaces.length === 1 && surfaces[0] === "desktop";
|
||||
const enabled = isPureDesktop || SettingsData.getPluginSetting(manifest.id, "enabled", false);
|
||||
if (enabled && !info.loaded)
|
||||
loadPlugin(manifest.id);
|
||||
} else {
|
||||
@@ -296,59 +350,70 @@ Singleton {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isDaemon = plugin.type === "daemon";
|
||||
const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
|
||||
const isDesktop = plugin.type === "desktop";
|
||||
const componentPaths = plugin.componentPaths || {};
|
||||
const surfaces = Object.keys(componentPaths);
|
||||
if (surfaces.length === 0) {
|
||||
log.error("Plugin has no component surfaces:", pluginId);
|
||||
pluginLoadFailed(pluginId, "No component surfaces");
|
||||
return false;
|
||||
}
|
||||
|
||||
const prevInstance = pluginInstances[pluginId];
|
||||
const newWidgets = Object.assign({}, pluginWidgetComponents);
|
||||
const newDesktop = Object.assign({}, pluginDesktopComponents);
|
||||
const newDaemons = Object.assign({}, pluginDaemonComponents);
|
||||
const newLaunchers = Object.assign({}, pluginLauncherComponents);
|
||||
const newInstances = Object.assign({}, pluginInstances);
|
||||
|
||||
const prevInstance = newInstances[pluginId];
|
||||
if (prevInstance) {
|
||||
prevInstance.destroy();
|
||||
const newInstances = Object.assign({}, pluginInstances);
|
||||
delete newInstances[pluginId];
|
||||
pluginInstances = newInstances;
|
||||
}
|
||||
|
||||
try {
|
||||
let url = "file://" + plugin.componentPath;
|
||||
if (bustCache)
|
||||
url += "?t=" + Date.now();
|
||||
const comp = Qt.createComponent(url, Component.PreferSynchronous);
|
||||
if (comp.status === Component.Error) {
|
||||
log.error("component error", pluginId, comp.errorString());
|
||||
pluginLoadFailed(pluginId, comp.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDaemon) {
|
||||
const newDaemons = Object.assign({}, pluginDaemonComponents);
|
||||
newDaemons[pluginId] = comp;
|
||||
pluginDaemonComponents = newDaemons;
|
||||
} else if (isLauncher) {
|
||||
const instance = comp.createObject(root, {
|
||||
"pluginService": root
|
||||
});
|
||||
if (!instance) {
|
||||
log.error("failed to instantiate plugin:", pluginId, comp.errorString());
|
||||
for (const surface of surfaces) {
|
||||
let url = "file://" + componentPaths[surface];
|
||||
if (bustCache)
|
||||
url += "?t=" + Date.now();
|
||||
const comp = Qt.createComponent(url, Component.PreferSynchronous);
|
||||
if (comp.status === Component.Error) {
|
||||
log.error("component error", pluginId, surface, comp.errorString());
|
||||
pluginLoadFailed(pluginId, comp.errorString());
|
||||
return false;
|
||||
}
|
||||
const newInstances = Object.assign({}, pluginInstances);
|
||||
newInstances[pluginId] = instance;
|
||||
pluginInstances = newInstances;
|
||||
|
||||
const newLaunchers = Object.assign({}, pluginLauncherComponents);
|
||||
newLaunchers[pluginId] = comp;
|
||||
pluginLauncherComponents = newLaunchers;
|
||||
} else if (isDesktop) {
|
||||
const newDesktop = Object.assign({}, pluginDesktopComponents);
|
||||
newDesktop[pluginId] = comp;
|
||||
pluginDesktopComponents = newDesktop;
|
||||
} else {
|
||||
const newComponents = Object.assign({}, pluginWidgetComponents);
|
||||
newComponents[pluginId] = comp;
|
||||
pluginWidgetComponents = newComponents;
|
||||
switch (surface) {
|
||||
case "daemon":
|
||||
newDaemons[pluginId] = comp;
|
||||
break;
|
||||
case "desktop":
|
||||
newDesktop[pluginId] = comp;
|
||||
break;
|
||||
case "launcher": {
|
||||
const instance = comp.createObject(root, {
|
||||
"pluginService": root
|
||||
});
|
||||
if (!instance) {
|
||||
log.error("failed to instantiate launcher surface:", pluginId, comp.errorString());
|
||||
pluginLoadFailed(pluginId, comp.errorString());
|
||||
return false;
|
||||
}
|
||||
newInstances[pluginId] = instance;
|
||||
newLaunchers[pluginId] = comp;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
newWidgets[pluginId] = comp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pluginWidgetComponents = newWidgets;
|
||||
pluginDesktopComponents = newDesktop;
|
||||
pluginDaemonComponents = newDaemons;
|
||||
pluginLauncherComponents = newLaunchers;
|
||||
pluginInstances = newInstances;
|
||||
|
||||
plugin.loaded = true;
|
||||
const newLoaded = Object.assign({}, loadedPlugins);
|
||||
newLoaded[pluginId] = plugin;
|
||||
@@ -371,10 +436,6 @@ Singleton {
|
||||
}
|
||||
|
||||
try {
|
||||
const isDaemon = plugin.type === "daemon";
|
||||
const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
|
||||
const isDesktop = plugin.type === "desktop";
|
||||
|
||||
const instance = pluginInstances[pluginId];
|
||||
if (instance) {
|
||||
instance.destroy();
|
||||
@@ -383,19 +444,22 @@ Singleton {
|
||||
pluginInstances = newInstances;
|
||||
}
|
||||
|
||||
if (isDaemon && pluginDaemonComponents[pluginId]) {
|
||||
if (pluginDaemonComponents[pluginId]) {
|
||||
const newDaemons = Object.assign({}, pluginDaemonComponents);
|
||||
delete newDaemons[pluginId];
|
||||
pluginDaemonComponents = newDaemons;
|
||||
} else if (isLauncher && pluginLauncherComponents[pluginId]) {
|
||||
}
|
||||
if (pluginLauncherComponents[pluginId]) {
|
||||
const newLaunchers = Object.assign({}, pluginLauncherComponents);
|
||||
delete newLaunchers[pluginId];
|
||||
pluginLauncherComponents = newLaunchers;
|
||||
} else if (isDesktop && pluginDesktopComponents[pluginId]) {
|
||||
}
|
||||
if (pluginDesktopComponents[pluginId]) {
|
||||
const newDesktop = Object.assign({}, pluginDesktopComponents);
|
||||
delete newDesktop[pluginId];
|
||||
pluginDesktopComponents = newDesktop;
|
||||
} else if (pluginWidgetComponents[pluginId]) {
|
||||
}
|
||||
if (pluginWidgetComponents[pluginId]) {
|
||||
const newComponents = Object.assign({}, pluginWidgetComponents);
|
||||
delete newComponents[pluginId];
|
||||
pluginWidgetComponents = newComponents;
|
||||
@@ -452,7 +516,8 @@ Singleton {
|
||||
const result = [];
|
||||
for (const pluginId in availablePlugins) {
|
||||
const plugin = availablePlugins[pluginId];
|
||||
if (plugin.type !== "widget") {
|
||||
const hasWidgetSurface = plugin.surfaces ? plugin.surfaces.includes("widget") : (plugin.type === "widget");
|
||||
if (!hasWidgetSurface) {
|
||||
continue;
|
||||
}
|
||||
const variants = getPluginVariants(pluginId);
|
||||
|
||||
Reference in New Issue
Block a user