Add context menu for manipulating watch panel

This commit is contained in:
baldurk
2018-06-22 17:58:56 +01:00
parent fc3e527181
commit ec2806df06
8 changed files with 239 additions and 82 deletions
+6
View File
@@ -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;
+29 -23
View File
@@ -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:
+56 -51
View File
@@ -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
+2 -1
View File
@@ -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;
+134 -5
View File
@@ -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(&copyValue);
contextMenu.addSeparator();
contextMenu.addAction(&addWatch);
if(QObject::sender() == ui->watch)
{
QObject::connect(&copyValue, &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(&copyValue, &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))
+3
View File
@@ -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);
+7 -2
View File
@@ -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>