Add a simple wizard to create a new empty extension

This commit is contained in:
baldurk
2026-08-13 17:46:44 +01:00
parent c97850e39b
commit a665def9d9
3 changed files with 211 additions and 1 deletions
@@ -31,6 +31,8 @@
#include "Code/Resources.h"
#include "Code/pyrenderdoc/PythonContext.h"
#include "Widgets/Extended/RDHeaderView.h"
#include "Widgets/Extended/RDLabel.h"
#include "Widgets/Extended/RDLineEdit.h"
#include "Windows/MainWindow.h"
#include "ui_ExtensionManager.h"
@@ -60,6 +62,13 @@ ExtensionManager::ExtensionManager(ICaptureContext &ctx)
QObject::connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
PopulateExtensionList();
}
void ExtensionManager::PopulateExtensionList()
{
ui->extensions->clear();
QString extensionFolder = ConfigFilePath("extensions");
m_Extensions = m_Ctx.Extensions().GetInstalledExtensions();
@@ -231,6 +240,176 @@ 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);
@@ -53,12 +53,15 @@ private slots:
void on_output_clicked();
void on_openLocation_clicked();
void on_alwaysLoad_toggled(bool checked);
void on_createExtension_clicked();
void on_extensions_currentItemChanged(RDTreeWidgetItem *item, RDTreeWidgetItem *);
void on_extensions_itemChanged(RDTreeWidgetItem *item, int col);
private:
void update_currentItem(RDTreeWidgetItem *item);
void PopulateExtensionList();
Ui::ExtensionManager *ui;
ICaptureContext &m_Ctx;
+29 -1
View File
@@ -8,6 +8,8 @@
<y>0</y>
<width>556</width>
<height>572</height>
<width>581</width>
<height>648</height>
</rect>
</property>
<property name="minimumSize">
@@ -34,7 +36,7 @@
<property name="title">
<string>Extension List</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout2">
<item>
<widget class="RDTreeWidget" name="extensions">
<property name="editTriggers">
@@ -48,6 +50,32 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="createContainer" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="createExtension">
<property name="text">
<string>Create New...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>