Move new extension creation wizard to python scripting

* We remove the operations and interactions from the extension manager and leave
  it purely for browsing extensions and enabling/disabling them.
This commit is contained in:
baldurk
2026-08-13 21:05:13 +01:00
parent 650188f32c
commit 4bd0245975
7 changed files with 211 additions and 327 deletions
+2 -265
View File
@@ -56,8 +56,6 @@ ExtensionManager::ExtensionManager(ICaptureContext &ctx)
ui->version->setText(lit("---"));
ui->author->setText(lit("---"));
ui->URL->setText(lit("---"));
ui->reload->setEnabled(false);
ui->debug->setEnabled(false);
ui->alwaysLoad->setEnabled(false);
QObject::connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
@@ -105,12 +103,8 @@ ExtensionManager::~ExtensionManager()
delete ui;
}
void ExtensionManager::on_reload_clicked()
void ExtensionManager::loadExtension(RDTreeWidgetItem *item)
{
RDTreeWidgetItem *item = ui->extensions->currentItem();
if(!item)
return;
int idx = ui->extensions->indexOfTopLevelItem(item);
if(idx >= 0 && idx < m_Extensions.count())
@@ -139,85 +133,6 @@ void ExtensionManager::on_reload_clicked()
}
}
void ExtensionManager::on_debug_clicked()
{
if(m_Extensions.empty())
return;
RDTreeWidgetItem *item = ui->extensions->currentItem();
if(!item)
return;
int idx = ui->extensions->indexOfTopLevelItem(item);
if(idx >= 0 && idx < m_Extensions.count())
{
const ExtensionMetadata &e = m_Extensions[idx];
if(!e.name.isEmpty())
{
PythonContext::PrepareDebuggerWait();
LambdaThread *thread = new LambdaThread([this]() {
PythonContext::WaitForDebugger();
GUIInvoke::call(this, [this]() { on_reload_clicked(); });
});
thread->selfDelete(true);
thread->start();
PythonContext::LaunchDebugger(this, m_Ctx.Config(), QFileInfo(e.filePath).absoluteFilePath());
}
}
}
void ExtensionManager::on_output_clicked()
{
m_Ctx.ShowPythonShell();
m_Ctx.GetPythonShell()->ShowOutput();
RDTreeWidgetItem *item = ui->extensions->currentItem();
if(item)
{
int idx = ui->extensions->indexOfTopLevelItem(item);
if(idx >= 0 && idx < m_Extensions.count())
{
const ExtensionMetadata &e = m_Extensions[idx];
if(!e.package.isEmpty())
{
m_Ctx.GetPythonShell()->SetExtensionOutputFilter(e.package);
}
}
}
accept();
}
void ExtensionManager::on_openLocation_clicked()
{
if(m_Extensions.empty())
{
QDesktopServices::openUrl(QString(ConfigFilePath("extensions")));
return;
}
RDTreeWidgetItem *item = ui->extensions->currentItem();
if(!item)
return;
int idx = ui->extensions->indexOfTopLevelItem(item);
if(idx >= 0 && idx < m_Extensions.count())
{
const ExtensionMetadata &e = m_Extensions[idx];
if(!e.name.isEmpty())
{
QDesktopServices::openUrl(QFileInfo(e.filePath).absoluteFilePath());
}
}
}
void ExtensionManager::on_alwaysLoad_toggled(bool checked)
{
RDTreeWidgetItem *item = ui->extensions->currentItem();
@@ -240,176 +155,6 @@ void ExtensionManager::on_alwaysLoad_toggled(bool checked)
}
}
void ExtensionManager::on_createExtension_clicked()
{
QDialog dialog;
RDLabel label;
RDLineEdit extensionName;
QDialogButtonBox buttons;
dialog.setWindowTitle(tr("Create new UI extension"));
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
label.setText(
tr("Create a new UI extension, with some example code.\n"
"\n"
"This will create the directory structure for the specified package name, with a default\n"
"extension metadata json and some simple example code to give you a starting point."));
extensionName.setPlaceholderText(tr("myname.example"));
buttons.setOrientation(Qt::Horizontal);
buttons.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons.setCenterButtons(true);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [this, &dialog, &extensionName]() {
QString extName = extensionName.text().trimmed();
if(extName.isEmpty())
{
RDDialog::critical(&dialog, tr("Invalid extension name"),
tr("Must specify a name for the new extension."));
return;
}
if(extName.startsWith(lit("renderdoc.")))
{
RDDialog::critical(&dialog, tr("Invalid extension name"),
tr("Extension name conflicts with builtin module 'renderdoc'."));
return;
}
if(extName.contains(QLatin1Char(' ')) || extName.contains(QLatin1Char('\t')))
{
RDDialog::critical(
&dialog, tr("Invalid extension name"),
tr("Extension names should be valid python package names, note including whitespace."));
return;
}
for(const ExtensionMetadata &e : m_Extensions)
{
if(QString(e.package) == extName)
{
RDDialog::critical(&dialog, tr("Extension name in use"),
tr("The extension name '%1' already exists.").arg(e.package));
return;
}
}
QStringList locations = PythonContext::GetApplicationExtensionsPaths();
if(!locations.empty())
{
QDir dir(locations[0]);
QStringList paths = extName.split(QLatin1Char('.'));
bool nonexist = false;
while(!paths.empty())
{
QString dirname = paths[0];
paths.pop_front();
if(!dir.cd(dirname))
{
nonexist = true;
break;
}
qInfo() << dir.absolutePath();
}
if(!nonexist && dir.exists() && !dir.isEmpty())
{
RDDialog::critical(&dialog, tr("Directory already exists"),
tr("Extension directory already exists:\n%1").arg(dir.absolutePath()));
return;
}
}
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
QVBoxLayout *layout = new QVBoxLayout(&dialog);
layout->addWidget(&label);
layout->addWidget(&extensionName);
layout->addWidget(&buttons);
if(!RDDialog::show(&dialog))
return;
if(dialog.result() == QDialog::Accepted)
{
QStringList locations = PythonContext::GetApplicationExtensionsPaths();
QDir dir(locations[0]);
QString extName = extensionName.text().trimmed();
QStringList paths = extName.split(QLatin1Char('.'));
while(!paths.empty())
{
QString dirname = paths[0];
paths.pop_front();
dir.mkdir(dirname);
if(!dir.cd(dirname))
{
RDDialog::critical(&dialog, tr("Couldn't create directory"),
tr("Failed to create %1 in %2").arg(dirname).arg(dir.absolutePath()));
return;
}
}
paths = extName.split(QLatin1Char('.'));
QString metadata = lit(R"({
"extension_api": 1,
"name": "%3",
"version": "1.0",
"minimum_renderdoc": "%1.%2",
"description": "Template extension %4",
"author": "My Name <my.email@example.com>",
"url": "https://github.com/example/example"
}
)")
.arg(RENDERDOC_VERSION_MAJOR)
.arg(RENDERDOC_VERSION_MINOR)
.arg(paths.back())
.arg(extName);
{
QFile ext(dir.absoluteFilePath(lit("extension.json")));
if(ext.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
ext.write(metadata.toUtf8());
}
QFile init(dir.absoluteFilePath(lit("__init__.py")));
if(init.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
init.write(R"(
# Blank RenderDoc UI extension
import renderdoc as rd
import qrenderdoc as qrd
def register(version: str, pyrenderdoc: qrd.CaptureContext):
print(f"New UI extension loaded in RenderDoc {version}")
def unregister():
print(f"New UI extension being unloaded")
)");
}
}
PopulateExtensionList();
}
}
void ExtensionManager::on_extensions_currentItemChanged(RDTreeWidgetItem *item, RDTreeWidgetItem *)
{
update_currentItem(item);
@@ -435,7 +180,7 @@ void ExtensionManager::on_extensions_itemChanged(RDTreeWidgetItem *item, int col
if(!loaded)
{
if(item->checkState(2) == Qt::Checked)
on_reload_clicked();
loadExtension(item);
}
}
}
@@ -474,14 +219,6 @@ void ExtensionManager::update_currentItem(RDTreeWidgetItem *item)
ui->author->setText(e.author);
bool loaded = item->checkState(2) == Qt::Checked;
ui->reload->setEnabled(true);
ui->reload->setText(loaded ? tr("Reload") : tr("Load"));
ui->output->setEnabled(loaded);
ui->debug->setEnabled(loaded && PythonContext::IsDebuggingEnabled());
ui->debug->setToolTip(QString());
if(loaded && !PythonContext::IsDebuggingEnabled())
ui->debug->setToolTip(
tr("Debugging not supported - check documentation for setup instructions"));
ui->alwaysLoad->setEnabled(loaded);
ui->alwaysLoad->setChecked(m_Ctx.Config().AlwaysLoad_Extensions.contains(e.package));