mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
Add context menu for manipulating watch panel
This commit is contained in:
@@ -557,6 +557,12 @@ struct IShaderViewer
|
||||
)");
|
||||
virtual void ShowErrors(const rdcstr &errors) = 0;
|
||||
|
||||
DOCUMENT(R"(Add an expression to the watch panel.
|
||||
|
||||
:param str expression: The name of the expression to watch.
|
||||
)");
|
||||
virtual void AddWatch(const rdcstr &expression) = 0;
|
||||
|
||||
protected:
|
||||
IShaderViewer() = default;
|
||||
~IShaderViewer() = default;
|
||||
|
||||
@@ -86,29 +86,8 @@ void RDTableWidget::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if(!m_customCopyPaste && e->matches(QKeySequence::Copy))
|
||||
{
|
||||
QList<QTableWidgetItem *> items = selectedItems();
|
||||
|
||||
std::sort(items.begin(), items.end(), [this](QTableWidgetItem *a, QTableWidgetItem *b) {
|
||||
if(row(a) != row(b))
|
||||
return row(a) < row(b);
|
||||
return column(a) < column(b);
|
||||
});
|
||||
|
||||
int prevRow = row(items[0]);
|
||||
|
||||
QString clipboardText;
|
||||
for(QTableWidgetItem *i : items)
|
||||
{
|
||||
clipboardText += i->text();
|
||||
|
||||
if(prevRow != row(i))
|
||||
clipboardText += lit("\n");
|
||||
else
|
||||
clipboardText += lit(" | ");
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipboardText.trimmed());
|
||||
copySelection();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -117,3 +96,30 @@ void RDTableWidget::keyPressEvent(QKeyEvent *e)
|
||||
|
||||
emit(keyPress(e));
|
||||
}
|
||||
|
||||
void RDTableWidget::copySelection()
|
||||
{
|
||||
QList<QTableWidgetItem *> items = selectedItems();
|
||||
|
||||
std::sort(items.begin(), items.end(), [this](QTableWidgetItem *a, QTableWidgetItem *b) {
|
||||
if(row(a) != row(b))
|
||||
return row(a) < row(b);
|
||||
return column(a) < column(b);
|
||||
});
|
||||
|
||||
int prevRow = row(items[0]);
|
||||
|
||||
QString clipboardText;
|
||||
for(QTableWidgetItem *i : items)
|
||||
{
|
||||
clipboardText += i->text();
|
||||
|
||||
if(prevRow != row(i))
|
||||
clipboardText += lit("\n");
|
||||
else
|
||||
clipboardText += lit(" | ");
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipboardText.trimmed());
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ class RDTableWidget : public QTableWidget
|
||||
public:
|
||||
explicit RDTableWidget(QWidget *parent = 0);
|
||||
|
||||
void copySelection();
|
||||
|
||||
bool customCopyPasteHandler() { return m_customCopyPaste; }
|
||||
void setCustomCopyPasteHandler(bool custom) { m_customCopyPaste = custom; }
|
||||
signals:
|
||||
|
||||
@@ -1025,57 +1025,7 @@ void RDTreeWidget::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if(!m_customCopyPaste && e->matches(QKeySequence::Copy))
|
||||
{
|
||||
QModelIndexList sel = selectionModel()->selectedRows();
|
||||
|
||||
int stackWidths[16];
|
||||
int *heapWidths = NULL;
|
||||
|
||||
int colCount = m_model->columnCount();
|
||||
|
||||
if(colCount >= 16)
|
||||
heapWidths = new int[colCount];
|
||||
|
||||
int *widths = heapWidths ? heapWidths : stackWidths;
|
||||
|
||||
for(int i = 0; i < colCount; i++)
|
||||
widths[i] = 0;
|
||||
|
||||
// align the copied data so that each column is the same width
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
RDTreeWidgetItem *item = m_model->itemForIndex(idx);
|
||||
|
||||
for(int i = 0; i < qMin(colCount, item->m_text.count()); i++)
|
||||
{
|
||||
QString text = item->m_text[i].toString();
|
||||
widths[i] = qMax(widths[i], text.count());
|
||||
}
|
||||
}
|
||||
|
||||
// only align up to 50 characters so one really long item doesn't mess up the whole thing
|
||||
for(int i = 0; i < colCount; i++)
|
||||
widths[i] = qMin(50, widths[i]);
|
||||
|
||||
QString clipData;
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
RDTreeWidgetItem *item = m_model->itemForIndex(idx);
|
||||
|
||||
for(int i = 0; i < qMin(colCount, item->m_text.count()); i++)
|
||||
{
|
||||
QString format = i == 0 ? QFormatStr("%1") : QFormatStr(" %1");
|
||||
QString text = item->m_text[i].toString();
|
||||
|
||||
clipData += format.arg(text, -widths[i]);
|
||||
}
|
||||
|
||||
clipData += lit("\n");
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipData.trimmed());
|
||||
|
||||
delete[] heapWidths;
|
||||
copySelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1083,6 +1033,61 @@ void RDTreeWidget::keyPressEvent(QKeyEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
void RDTreeWidget::copySelection()
|
||||
{
|
||||
QModelIndexList sel = selectionModel()->selectedRows();
|
||||
|
||||
int stackWidths[16];
|
||||
int *heapWidths = NULL;
|
||||
|
||||
int colCount = m_model->columnCount();
|
||||
|
||||
if(colCount >= 16)
|
||||
heapWidths = new int[colCount];
|
||||
|
||||
int *widths = heapWidths ? heapWidths : stackWidths;
|
||||
|
||||
for(int i = 0; i < colCount; i++)
|
||||
widths[i] = 0;
|
||||
|
||||
// align the copied data so that each column is the same width
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
RDTreeWidgetItem *item = m_model->itemForIndex(idx);
|
||||
|
||||
for(int i = 0; i < qMin(colCount, item->m_text.count()); i++)
|
||||
{
|
||||
QString text = item->m_text[i].toString();
|
||||
widths[i] = qMax(widths[i], text.count());
|
||||
}
|
||||
}
|
||||
|
||||
// only align up to 50 characters so one really long item doesn't mess up the whole thing
|
||||
for(int i = 0; i < colCount; i++)
|
||||
widths[i] = qMin(50, widths[i]);
|
||||
|
||||
QString clipData;
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
RDTreeWidgetItem *item = m_model->itemForIndex(idx);
|
||||
|
||||
for(int i = 0; i < qMin(colCount, item->m_text.count()); i++)
|
||||
{
|
||||
QString format = i == 0 ? QFormatStr("%1") : QFormatStr(" %1");
|
||||
QString text = item->m_text[i].toString();
|
||||
|
||||
clipData += format.arg(text, -widths[i]);
|
||||
}
|
||||
|
||||
clipData += lit("\n");
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipData.trimmed());
|
||||
|
||||
delete[] heapWidths;
|
||||
}
|
||||
|
||||
void RDTreeWidget::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
|
||||
{
|
||||
// we do our own custom branch rendering to ensure the backgrounds for the +/- markers are
|
||||
|
||||
@@ -279,6 +279,8 @@ public:
|
||||
void collapseAllItems(RDTreeWidgetItem *item);
|
||||
void scrollToItem(RDTreeWidgetItem *node);
|
||||
|
||||
void copySelection();
|
||||
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
@@ -298,7 +300,6 @@ private:
|
||||
void leaveEvent(QEvent *e) override;
|
||||
void focusOutEvent(QFocusEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override;
|
||||
|
||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
|
||||
|
||||
@@ -563,6 +563,16 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
|
||||
|
||||
QObject::connect(ui->watch, &RDTableWidget::keyPress, this, &ShaderViewer::watch_keyPress);
|
||||
|
||||
ui->watch->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QObject::connect(ui->watch, &RDTableWidget::customContextMenuRequested, this,
|
||||
&ShaderViewer::variables_contextMenu);
|
||||
ui->registers->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QObject::connect(ui->registers, &RDTreeWidget::customContextMenuRequested, this,
|
||||
&ShaderViewer::variables_contextMenu);
|
||||
ui->locals->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QObject::connect(ui->locals, &RDTreeWidget::customContextMenuRequested, this,
|
||||
&ShaderViewer::variables_contextMenu);
|
||||
|
||||
ui->watch->insertRow(0);
|
||||
|
||||
for(int i = 0; i < ui->watch->columnCount(); i++)
|
||||
@@ -955,6 +965,87 @@ void ShaderViewer::debug_contextMenu(const QPoint &pos)
|
||||
RDDialog::show(&contextMenu, edit->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void ShaderViewer::variables_contextMenu(const QPoint &pos)
|
||||
{
|
||||
QAbstractItemView *w = qobject_cast<QAbstractItemView *>(QObject::sender());
|
||||
|
||||
QMenu contextMenu(this);
|
||||
|
||||
QAction copyValue(tr("Copy"), this);
|
||||
QAction addWatch(tr("Add Watch"), this);
|
||||
QAction deleteWatch(tr("Delete Watch"), this);
|
||||
QAction clearAll(tr("Clear All"), this);
|
||||
|
||||
contextMenu.addAction(©Value);
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.addAction(&addWatch);
|
||||
|
||||
if(QObject::sender() == ui->watch)
|
||||
{
|
||||
QObject::connect(©Value, &QAction::triggered, [this] { ui->watch->copySelection(); });
|
||||
|
||||
contextMenu.addAction(&deleteWatch);
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.addAction(&clearAll);
|
||||
|
||||
// start with no row selected
|
||||
int selRow = -1;
|
||||
|
||||
QList<QTableWidgetItem *> items = ui->watch->selectedItems();
|
||||
for(QTableWidgetItem *item : items)
|
||||
{
|
||||
// if no row is selected, or the same as this item, set selected row to this item's
|
||||
if(selRow == -1 || selRow == item->row())
|
||||
{
|
||||
selRow = item->row();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we only get here if we see an item on a different row selected - that means too many rows
|
||||
// so bail out
|
||||
selRow = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a selected row that isn't the last one, we can add/delete this item
|
||||
deleteWatch.setEnabled(selRow >= 0 && selRow < ui->watch->rowCount() - 1);
|
||||
addWatch.setEnabled(selRow >= 0 && selRow < ui->watch->rowCount() - 1);
|
||||
|
||||
QObject::connect(&addWatch, &QAction::triggered, [this, selRow] {
|
||||
QTableWidgetItem *item = ui->watch->item(selRow, 0);
|
||||
|
||||
if(item)
|
||||
AddWatch(item->text());
|
||||
});
|
||||
|
||||
QObject::connect(&deleteWatch, &QAction::triggered,
|
||||
[this, selRow] { ui->watch->removeRow(selRow); });
|
||||
|
||||
QObject::connect(&clearAll, &QAction::triggered, [this] {
|
||||
while(ui->watch->rowCount() > 1)
|
||||
ui->watch->removeRow(0);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
RDTreeWidget *tree = qobject_cast<RDTreeWidget *>(w);
|
||||
|
||||
QObject::connect(©Value, &QAction::triggered, [this, tree] { tree->copySelection(); });
|
||||
|
||||
addWatch.setEnabled(tree->selectedItem() != NULL);
|
||||
|
||||
QObject::connect(&addWatch, &QAction::triggered, [this, tree] {
|
||||
if(tree == ui->locals)
|
||||
AddWatch(tree->selectedItem()->tag().toString());
|
||||
else
|
||||
AddWatch(tree->selectedItem()->text(0));
|
||||
});
|
||||
}
|
||||
|
||||
RDDialog::show(&contextMenu, w->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event)
|
||||
{
|
||||
if(event->button() == Qt::LeftButton)
|
||||
@@ -2066,7 +2157,6 @@ void ShaderViewer::updateDebugging()
|
||||
for(int i = 0; i < ui->watch->rowCount() - 1; i++)
|
||||
{
|
||||
QTableWidgetItem *item = ui->watch->item(i, 0);
|
||||
ui->watch->setItem(i, 1, new QTableWidgetItem(tr("register", "watch type")));
|
||||
|
||||
QString reg = item->text().trimmed();
|
||||
|
||||
@@ -2084,6 +2174,10 @@ void ShaderViewer::updateDebugging()
|
||||
|
||||
if(match.hasMatch())
|
||||
{
|
||||
item = new QTableWidgetItem(tr("register", "watch type"));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 2, item);
|
||||
|
||||
QString regtype = match.captured(1);
|
||||
QString regidx = match.captured(2);
|
||||
QString swizzle = match.captured(3).replace(QLatin1Char('.'), QString());
|
||||
@@ -2189,10 +2283,15 @@ void ShaderViewer::updateDebugging()
|
||||
val += lit(", ");
|
||||
}
|
||||
|
||||
item = new QTableWidgetItem(vr.name);
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 1, item);
|
||||
|
||||
item = new QTableWidgetItem(val);
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(VariableTag(varCat, regindex, arrIndex)));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
|
||||
ui->watch->setItem(i, 2, item);
|
||||
ui->watch->setItem(i, 3, item);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -2213,14 +2312,26 @@ void ShaderViewer::updateDebugging()
|
||||
{
|
||||
// TODO apply swizzle/typecast ?
|
||||
|
||||
item = new QTableWidgetItem(local->text(1));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 1, item);
|
||||
|
||||
item = new QTableWidgetItem(local->text(2));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 2, item);
|
||||
|
||||
if(local->childCount() > 0)
|
||||
{
|
||||
// can't display structs
|
||||
ui->watch->setItem(i, 2, new QTableWidgetItem(lit("{...}")));
|
||||
item = new QTableWidgetItem(lit("{...}"));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 3, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->watch->setItem(i, 2, new QTableWidgetItem(local->text(3)));
|
||||
item = new QTableWidgetItem(local->text(3));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 3, item);
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -2228,7 +2339,13 @@ void ShaderViewer::updateDebugging()
|
||||
}
|
||||
}
|
||||
|
||||
ui->watch->setItem(i, 2, new QTableWidgetItem(tr("Error evaluating expression")));
|
||||
item = new QTableWidgetItem();
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 2, item);
|
||||
|
||||
item = new QTableWidgetItem(tr("Error evaluating expression"));
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->watch->setItem(i, 3, item);
|
||||
}
|
||||
|
||||
ui->watch->setUpdatesEnabled(true);
|
||||
@@ -2417,6 +2534,18 @@ void ShaderViewer::ShowErrors(const rdcstr &errors)
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderViewer::AddWatch(const rdcstr &variable)
|
||||
{
|
||||
int newRow = ui->watch->rowCount() - 1;
|
||||
ui->watch->insertRow(ui->watch->rowCount() - 1);
|
||||
|
||||
ui->watch->setItem(newRow, 0, new QTableWidgetItem(variable));
|
||||
|
||||
ToolWindowManager::raiseToolWindow(ui->watch);
|
||||
ui->watch->activateWindow();
|
||||
ui->watch->QWidget::setFocus();
|
||||
}
|
||||
|
||||
int ShaderViewer::snippetPos()
|
||||
{
|
||||
if(IsD3D(m_Ctx.APIProps().pipelineType))
|
||||
|
||||
@@ -100,6 +100,8 @@ public:
|
||||
|
||||
virtual void ShowErrors(const rdcstr &errors) override;
|
||||
|
||||
virtual void AddWatch(const rdcstr &variable) override;
|
||||
|
||||
// ICaptureViewer
|
||||
void OnCaptureLoaded() override;
|
||||
void OnCaptureClosed() override;
|
||||
@@ -120,6 +122,7 @@ private slots:
|
||||
void readonly_keyPressed(QKeyEvent *event);
|
||||
void editable_keyPressed(QKeyEvent *event);
|
||||
void debug_contextMenu(const QPoint &pos);
|
||||
void variables_contextMenu(const QPoint &pos);
|
||||
void disassembly_buttonReleased(QMouseEvent *event);
|
||||
void disassemble_typeChanged(int index);
|
||||
void watch_keyPress(QKeyEvent *event);
|
||||
|
||||
@@ -509,9 +509,9 @@
|
||||
<widget class="RDTableWidget" name="watch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>790</x>
|
||||
<x>520</x>
|
||||
<y>60</y>
|
||||
<width>151</width>
|
||||
<width>421</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -544,6 +544,11 @@
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Register(s)</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
|
||||
Reference in New Issue
Block a user