mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 15:24:00 -04:00
plugins: hide the list under the detail overlay
plugins: block activation when the startup check cannot run ref #3626
This commit is contained in:
+1
-1
Submodule dank-qml-common updated: 334c32a814...2d206c20d1
Generated
+3
-3
@@ -3,11 +3,11 @@
|
||||
"dank-qml-common": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1790713744,
|
||||
"narHash": "sha256-hDpFEPmzXG/7xHnoRFwQMIEjYDJ59dwjUKsqLR9GGvg=",
|
||||
"lastModified": 1790715373,
|
||||
"narHash": "sha256-hiykGTIlWoRLntC4BWTZ/HIfTQ1aABmAvDyu9t3Sb+A=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dank-qml-common",
|
||||
"rev": "334c32a814b5129d47ca757c29db3071df5620ea",
|
||||
"rev": "2d206c20d102fb77edf35a41b098f9cc086d1acf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -846,7 +846,9 @@ RegistryBrowserWindow {
|
||||
}
|
||||
]
|
||||
|
||||
overlay: Rectangle {
|
||||
overlayActive: detailPluginId !== ""
|
||||
|
||||
overlay: Item {
|
||||
id: detailPane
|
||||
|
||||
property var plugin: ({})
|
||||
@@ -859,23 +861,7 @@ RegistryBrowserWindow {
|
||||
plugin = livePlugin;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
anchors.bottom: parent.bottom
|
||||
z: 10
|
||||
color: Theme.floatingWindowSurface
|
||||
opacity: root.detailPluginId !== "" ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
anchors.fill: parent
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
|
||||
@@ -19,7 +19,8 @@ DankFloatingWindow {
|
||||
property alias aboveSearch: aboveSearchSlot.data
|
||||
property alias belowSearch: belowSearchSlot.data
|
||||
property alias listContent: listSlot.data
|
||||
property alias overlay: browserContent.data
|
||||
property alias overlay: overlaySlot.data
|
||||
property bool overlayActive: false
|
||||
readonly property alias installConfirm: urlInstallConfirm
|
||||
readonly property alias listArea: listSlot
|
||||
|
||||
@@ -189,58 +190,80 @@ DankFloatingWindow {
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.bottomMargin: Theme.spacingL
|
||||
|
||||
Column {
|
||||
id: aboveSearchSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Theme.spacingS
|
||||
}
|
||||
Item {
|
||||
id: browserBody
|
||||
anchors.fill: parent
|
||||
opacity: 1 - overlaySlot.opacity
|
||||
visible: opacity > 0
|
||||
|
||||
DankSearchField {
|
||||
id: browserSearchField
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: aboveSearchSlot.bottom
|
||||
anchors.topMargin: aboveSearchSlot.height > 0 ? Theme.spacingM : 0
|
||||
height: Theme.fieldHeightLarge
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
placeholderText: root.searchPlaceholder
|
||||
text: root.searchQuery
|
||||
focus: true
|
||||
ignoreLeftRightKeys: true
|
||||
keyForwardTargets: [browserKeyHandler]
|
||||
onTextEdited: {
|
||||
root.searchQuery = text;
|
||||
root.applySearch();
|
||||
Column {
|
||||
id: aboveSearchSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Theme.spacingS
|
||||
}
|
||||
|
||||
DankSearchField {
|
||||
id: browserSearchField
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: aboveSearchSlot.bottom
|
||||
anchors.topMargin: aboveSearchSlot.height > 0 ? Theme.spacingM : 0
|
||||
height: Theme.fieldHeightLarge
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
placeholderText: root.searchPlaceholder
|
||||
text: root.searchQuery
|
||||
focus: true
|
||||
ignoreLeftRightKeys: true
|
||||
keyForwardTargets: [browserKeyHandler]
|
||||
onTextEdited: {
|
||||
root.searchQuery = text;
|
||||
root.applySearch();
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: belowSearchSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: browserSearchField.bottom
|
||||
anchors.topMargin: height > 0 ? Theme.spacingM : 0
|
||||
spacing: Theme.spacingS
|
||||
}
|
||||
|
||||
Item {
|
||||
id: listSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: belowSearchSlot.bottom
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: root.isLoading
|
||||
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
running: root.isLoading
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: belowSearchSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: browserSearchField.bottom
|
||||
anchors.topMargin: height > 0 ? Theme.spacingM : 0
|
||||
spacing: Theme.spacingS
|
||||
}
|
||||
|
||||
Item {
|
||||
id: listSlot
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: belowSearchSlot.bottom
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.bottom: parent.bottom
|
||||
id: overlaySlot
|
||||
anchors.fill: parent
|
||||
opacity: root.overlayActive ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: root.isLoading
|
||||
|
||||
DankSpinner {
|
||||
anchors.centerIn: parent
|
||||
running: root.isLoading
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ QtObject {
|
||||
}
|
||||
```
|
||||
|
||||
A synchronous variant is supported too - declare `check()` with no argument and return the result directly. When the check fails the enable toggle reverts and the error is shown as a toast (the `details` are expandable, and any `http(s)` URL in them becomes a clickable link). Plugins without a `startupCheck` are unaffected. See `ExampleStartupCheck` for a complete plugin; the last error per plugin is available at `PluginService.pluginLoadErrors[pluginId]`.
|
||||
A synchronous variant is supported too - declare `check()` with no argument and return the result directly. When the check fails the enable toggle reverts and the error is shown as a toast (the `details` are expandable, and any `http(s)` URL in them becomes a clickable link). A check that fails to load, has no `check` function, or throws blocks activation the same way, with its error as the message. The check also runs again on reload. Plugins without a `startupCheck` are unaffected. See `ExampleStartupCheck` for a complete plugin; the last error per plugin is available at `PluginService.pluginLoadErrors[pluginId]`.
|
||||
|
||||
## Plugin Global Variables
|
||||
|
||||
|
||||
@@ -893,10 +893,8 @@ Singleton {
|
||||
|
||||
function _makeStartupCheckObject(pluginId, plugin) {
|
||||
const comp = Qt.createComponent(pluginComponentUrl(pluginId, plugin.startupCheckPath), Component.PreferSynchronous);
|
||||
if (comp.status === Component.Error) {
|
||||
log.error("startupCheck component error", pluginId, comp.errorString());
|
||||
return null;
|
||||
}
|
||||
if (comp.status === Component.Error)
|
||||
throw new Error(comp.errorString());
|
||||
return comp.createObject(root);
|
||||
}
|
||||
|
||||
@@ -915,8 +913,12 @@ Singleton {
|
||||
return ok;
|
||||
}
|
||||
|
||||
const probe = _makeStartupCheckObject(pluginId, plugin);
|
||||
let probe = null;
|
||||
let settled = false;
|
||||
const finish = result => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
if (probe)
|
||||
probe.destroy();
|
||||
const err = _normalizeStartupError(result);
|
||||
@@ -937,28 +939,20 @@ Singleton {
|
||||
onResult(ok);
|
||||
};
|
||||
|
||||
const check = probe ? probe.check : null;
|
||||
if (typeof check !== "function") {
|
||||
finish(null);
|
||||
return true;
|
||||
}
|
||||
if (check.length >= 1) {
|
||||
try {
|
||||
check(finish);
|
||||
} catch (e) {
|
||||
log.warn("startupCheck threw for", pluginId, e.message);
|
||||
finish(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
let r = null;
|
||||
try {
|
||||
r = check();
|
||||
probe = _makeStartupCheckObject(pluginId, plugin);
|
||||
const check = probe?.check;
|
||||
if (typeof check !== "function")
|
||||
throw new Error("startupCheck has no check function");
|
||||
if (check.length >= 1)
|
||||
check(finish);
|
||||
else
|
||||
finish(check());
|
||||
} catch (e) {
|
||||
log.warn("startupCheck threw for", pluginId, e.message);
|
||||
r = null;
|
||||
const message = String(e?.message || e);
|
||||
log.warn("startupCheck failed for", pluginId, message);
|
||||
finish(message);
|
||||
}
|
||||
finish(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -973,7 +967,7 @@ Singleton {
|
||||
const plugin = availablePlugins[pluginId];
|
||||
if (plugin)
|
||||
_loadPluginTranslations(pluginId, plugin.pluginDirectory);
|
||||
return loadPlugin(pluginId);
|
||||
return runStartupGate(pluginId);
|
||||
}
|
||||
|
||||
function ensureLauncherInstance(pluginId) {
|
||||
|
||||
@@ -176,6 +176,33 @@ test("updated startup checks still prevent activation on failure", () => {
|
||||
context.forceRescanPlugin("fixture");
|
||||
pending.shift()();
|
||||
assert.equal(context.isPluginLoaded("fixture"), true);
|
||||
files.set("file:///plugins/fixture/Check.qml", {
|
||||
check: () => "dependency removed",
|
||||
destroy() {}
|
||||
});
|
||||
context.reloadPlugin("fixture");
|
||||
assert.equal(context.isPluginLoaded("fixture"), false);
|
||||
assert.equal(failures.at(-1), "dependency removed");
|
||||
});
|
||||
|
||||
test("startup checks that cannot run prevent activation", () => {
|
||||
const { context, files, pending, manifest, failures } = service();
|
||||
const createComponent = context.Qt.createComponent;
|
||||
context.Qt.createComponent = url => url.startsWith("file:///plugins/fixture/Broken.qml") ? { status: 3, errorString: () => "module missing" } : createComponent(url);
|
||||
const cases = [
|
||||
["./Broken.qml", null, "module missing"],
|
||||
["./Check.qml", { destroy() {} }, "startupCheck has no check function"],
|
||||
["./Check.qml", { check() { throw new Error("sync throw"); }, destroy() {} }, "sync throw"],
|
||||
["./Check.qml", { check(done) { throw new Error("async throw"); }, destroy() {} }, "async throw"]
|
||||
];
|
||||
for (const [path, probe, error] of cases) {
|
||||
manifest.startupCheck = path;
|
||||
files.set("file:///plugins/fixture/Check.qml", probe);
|
||||
context.forceRescanPlugin("fixture");
|
||||
pending.shift()();
|
||||
assert.equal(context.isPluginLoaded("fixture"), false, error);
|
||||
assert.equal(failures.at(-1), error);
|
||||
}
|
||||
});
|
||||
|
||||
test("composite updates refresh every surface without creating daemons synchronously", () => {
|
||||
|
||||
Reference in New Issue
Block a user