Lua: Cleanup InfoBar entries

Also changes InfoBarDisplay::update to completely disconnect the resulting
widgets when an entry is removed.

Change-Id: Ic32ee8a1c9ee8dcd026e4a0cb7521b07323ca892
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Marcus Tillmanns
2025-01-28 15:49:11 +01:00
parent aafded8d33
commit 49fe642721
5 changed files with 214 additions and 166 deletions

View File

@@ -264,10 +264,20 @@ void InfoBarDisplay::infoBarDestroyed()
// will delete the widgets itself) or setInfoBar() being called explicitly.
}
static void disconnectRecursively(QObject *obj)
{
obj->disconnect();
for (QObject *child : obj->children())
disconnectRecursively(child);
}
void InfoBarDisplay::update()
{
for (QWidget *widget : std::as_const(m_infoWidgets)) {
widget->disconnect(this); // We want no destroyed() signal now
// Make sure that we are no longer connect to anything (especially lambdas).
// Otherwise a lambda might live longer than the owner of the lambda.
disconnectRecursively(widget);
widget->hide(); // Late deletion can cause duplicate infos. Hide immediately to prevent it.
widget->deleteLater();
}

View File

@@ -4,6 +4,8 @@
#include "../luaengine.h"
#include "../luatr.h"
#include "utils.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
@@ -135,7 +137,10 @@ void setupFetchModule()
std::shared_ptr<Module> module = std::make_shared<Module>();
registerProvider("Fetch", [mod = std::move(module)](sol::state_view lua) -> sol::object {
registerProvider(
"Fetch",
[mod = std::move(module),
infoBarCleaner = InfoBarCleaner()](sol::state_view lua) mutable -> sol::object {
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
sol::table async = lua.script("return require('async')", "_fetch_").get<sol::table>();
@@ -158,6 +163,7 @@ void setupFetchModule()
});
auto checkPermission = [mod,
&infoBarCleaner,
pluginName = pluginSpec->name,
guard = pluginSpec->connectionGuard.get()](
QString url,
@@ -195,7 +201,10 @@ void setupFetchModule()
});
QObject::connect(
msgBox, &QMessageBox::rejected, guard, [mod, notAllowed, pluginName, msgBox]() {
msgBox,
&QMessageBox::rejected,
guard,
[mod, notAllowed, pluginName, msgBox]() {
if (msgBox->checkBox()->isChecked())
mod->setAllowedToFetch(pluginName, Module::IsAllowed::No);
notAllowed();
@@ -206,8 +215,11 @@ void setupFetchModule()
return;
}
const Id infoBarId = Id("Fetch").withSuffix(pluginName);
infoBarCleaner.infoBarEntryAdded(infoBarId);
InfoBarEntry entry{
Id("Fetch").withSuffix(pluginName),
infoBarId,
Tr::tr("Allow the extension \"%1\" to fetch data from the internet?")
.arg(pluginName)};
entry.setDetailsWidgetCreator([pluginName, url] {
@@ -244,8 +256,8 @@ void setupFetchModule()
pluginName = pluginSpec->name,
guard = pluginSpec->connectionGuard.get(),
mod](
const sol::table &options,
const sol::function &callback,
const sol::main_table &options,
const sol::main_function &callback,
const sol::this_state &thisState) {
auto url = options.get<QString>("url");
auto actualFetch = [guard, url, options, callback, thisState]() {
@@ -275,7 +287,8 @@ void setupFetchModule()
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
callback(QString("%1 (%2):\n%3")
callback(
QString("%1 (%2):\n%3")
.arg(reply->errorString())
.arg(QString::fromLatin1(
QMetaEnum::fromType<QNetworkReply::NetworkError>()
@@ -296,8 +309,7 @@ void setupFetchModule()
});
} else {
QObject::connect(
reply, &QNetworkReply::finished, guard, [reply, callback]() {
QObject::connect(reply, &QNetworkReply::finished, guard, [reply, callback]() {
// We don't want the network reply to be deleted by the manager, but
// by the Lua GC
reply->setParent(nullptr);
@@ -307,7 +319,8 @@ void setupFetchModule()
};
checkPermission(url, actualFetch, [callback, pluginName]() {
callback(Tr::tr("Fetching is not allowed for the extension \"%1\". (You can edit "
callback(
Tr::tr("Fetching is not allowed for the extension \"%1\". (You can edit "
"permissions in Preferences > Lua.)")
.arg(pluginName));
});

View File

@@ -4,6 +4,8 @@
#include "../luaengine.h"
#include "../luatr.h"
#include "utils.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/progressmanager/taskprogress.h>
@@ -255,7 +257,9 @@ void setupInstallModule()
};
registerProvider(
"Install", [state = State()](sol::state_view lua) mutable -> sol::object {
"Install",
[state = State(),
infoBarCleaner = InfoBarCleaner()](sol::state_view lua) mutable -> sol::object {
sol::table async
= lua.script("return require('async')", "_install_async_").get<sol::table>();
sol::function wrap = async["wrap"];
@@ -281,7 +285,7 @@ void setupInstallModule()
};
install["install_cb"] =
[pluginSpec, &state](
[pluginSpec, &state, &infoBarCleaner](
const QString &msg,
const sol::table &installOptions,
const sol::function &callback) {
@@ -354,6 +358,8 @@ void setupInstallModule()
.withSuffix(pluginSpec->name)
.withSuffix(QString::number(qHash(installOptionsList)));
infoBarCleaner.infoBarEntryAdded(infoBarId);
InfoBarEntry entry(infoBarId, msg, InfoBarEntry::GlobalSuppression::Enabled);
entry.addCustomButton(Tr::tr("Install"), [install, infoBarId]() {

View File

@@ -4,6 +4,8 @@
#include "../luaengine.h"
#include "../luaqttypes.h"
#include <coreplugin/icore.h>
#include "utils.h"
#include <utils/async.h>
@@ -12,6 +14,7 @@
#include <utils/hostosinfo.h>
#include <utils/icon.h>
#include <utils/id.h>
#include <utils/infobar.h>
#include <utils/processinterface.h>
#include <QDesktopServices>
@@ -318,4 +321,10 @@ void setupUtilsModule()
});
}
InfoBarCleaner::~InfoBarCleaner()
{
for (const auto &id : openInfoBars)
Core::ICore::infoBar()->removeInfo(id);
}
} // namespace Lua::Internal

View File

@@ -7,6 +7,7 @@
#include <utils/filepath.h>
#include <utils/icon.h>
#include <utils/id.h>
#include <QMetaEnum>
@@ -61,4 +62,13 @@ inline QFlags<E> tableToFlags(const sol::table &table) noexcept {
return flags;
}
class InfoBarCleaner
{
QList<Utils::Id> openInfoBars;
public:
~InfoBarCleaner();
void infoBarEntryAdded(const Utils::Id &id) { openInfoBars.append(id); }
};
} // namespace Lua::Internal