mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 09:30:44 +00:00
Improve copy-paste behaviour for tree views
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "RDTreeView.h"
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
@@ -31,6 +33,51 @@
|
||||
#include <QToolTip>
|
||||
#include <QWheelEvent>
|
||||
|
||||
static int GetDepth(const QAbstractItemModel *model, const QModelIndex &idx)
|
||||
{
|
||||
if(idx == QModelIndex())
|
||||
return 0;
|
||||
|
||||
return 1 + GetDepth(model, model->parent(idx));
|
||||
}
|
||||
|
||||
static bool CompareModelIndex(const QModelIndex &a, const QModelIndex &b)
|
||||
{
|
||||
if(a == b)
|
||||
return false;
|
||||
|
||||
if(a == QModelIndex())
|
||||
return true;
|
||||
else if(b == QModelIndex())
|
||||
return false;
|
||||
|
||||
if(a.model() != b.model())
|
||||
return false;
|
||||
|
||||
QModelIndex ap = a.model()->parent(a);
|
||||
QModelIndex bp = b.model()->parent(b);
|
||||
if(ap == bp)
|
||||
{
|
||||
if(a.row() == b.row())
|
||||
return a.column() < b.column();
|
||||
return a.row() < b.row();
|
||||
}
|
||||
|
||||
if(a == bp)
|
||||
return true;
|
||||
if(b == ap)
|
||||
return false;
|
||||
|
||||
int ad = GetDepth(a.model(), a);
|
||||
int bd = GetDepth(b.model(), b);
|
||||
|
||||
if(ad > bd)
|
||||
return CompareModelIndex(ap, b);
|
||||
else if(ad < bd)
|
||||
return CompareModelIndex(a, bp);
|
||||
return CompareModelIndex(ap, bp);
|
||||
}
|
||||
|
||||
RDTreeViewDelegate::RDTreeViewDelegate(RDTreeView *view) : ForwardingDelegate(view), m_View(view)
|
||||
{
|
||||
}
|
||||
@@ -167,7 +214,14 @@ void RDTreeView::leaveEvent(QEvent *e)
|
||||
|
||||
void RDTreeView::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
QTreeView::keyPressEvent(e);
|
||||
if(e->matches(QKeySequence::Copy))
|
||||
{
|
||||
copySelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
QTreeView::keyPressEvent(e);
|
||||
}
|
||||
emit(keyPress(e));
|
||||
}
|
||||
|
||||
@@ -262,6 +316,74 @@ void RDTreeView::applyExpansion(const RDTreeViewExpansionState &state, const Exp
|
||||
applyExpansionToRow(state, model()->index(i, 0), 0, keygen);
|
||||
}
|
||||
|
||||
void RDTreeView::copySelection()
|
||||
{
|
||||
QModelIndexList sel = selectionModel()->selectedRows();
|
||||
|
||||
std::sort(sel.begin(), sel.end(), CompareModelIndex);
|
||||
|
||||
QVector<int> widths;
|
||||
|
||||
ICaptureContext *ctx = getCaptureContext(this);
|
||||
|
||||
int minDepth = INT_MAX;
|
||||
int maxDepth = 0;
|
||||
|
||||
// align the copied data so that each column is the same width
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
int colCount = model()->columnCount(idx);
|
||||
|
||||
widths.resize(qMax(widths.size(), colCount));
|
||||
|
||||
for(int i = 0; i < colCount; i++)
|
||||
{
|
||||
QVariant var = model()->data(model()->index(idx.row(), i, idx.parent()));
|
||||
QString text = ctx ? RichResourceTextFormat(*ctx, var) : var.toString();
|
||||
widths[i] = qMax(widths[i], text.count());
|
||||
}
|
||||
|
||||
int depth = GetDepth(model(), idx);
|
||||
|
||||
minDepth = qMin(minDepth, depth);
|
||||
maxDepth = qMax(maxDepth, depth);
|
||||
}
|
||||
|
||||
// add on two characters for every depth, for indent
|
||||
for(int &i : widths)
|
||||
i += 2 * (maxDepth - minDepth - 1);
|
||||
|
||||
// only align up to 50 characters so one really long item doesn't mess up the whole thing
|
||||
for(int &i : widths)
|
||||
i = qMin(50, i);
|
||||
|
||||
QString clipData;
|
||||
for(QModelIndex idx : sel)
|
||||
{
|
||||
int colCount = model()->columnCount(idx);
|
||||
|
||||
int depth = GetDepth(model(), idx);
|
||||
|
||||
for(int i = 0; i < colCount; i++)
|
||||
{
|
||||
QString format = i == 0 ? QFormatStr("%1") : QFormatStr(" %1");
|
||||
|
||||
QVariant var = model()->data(model()->index(idx.row(), i, idx.parent()));
|
||||
QString text = ctx ? RichResourceTextFormat(*ctx, var) : var.toString();
|
||||
|
||||
if(i == 0)
|
||||
text.prepend(QString((depth - minDepth) * 2, QLatin1Char(' ')));
|
||||
|
||||
clipData += format.arg(text, -widths[i]);
|
||||
}
|
||||
|
||||
clipData += lit("\n");
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(clipData.trimmed());
|
||||
}
|
||||
|
||||
void RDTreeView::saveExpansionFromRow(RDTreeViewExpansionState &state, QModelIndex idx, uint seed,
|
||||
const ExpansionKeyGen &keygen)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,8 @@ typedef std::function<uint(QModelIndex, uint)> ExpansionKeyGen;
|
||||
class RDTreeView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool customCopyPasteHandler READ customCopyPasteHandler WRITE setCustomCopyPasteHandler)
|
||||
public:
|
||||
explicit RDTreeView(QWidget *parent = 0);
|
||||
virtual ~RDTreeView();
|
||||
@@ -87,6 +89,8 @@ public:
|
||||
int verticalItemMargin() { return m_VertMargin; }
|
||||
void setIgnoreIconSize(bool ignore) { m_IgnoreIconSize = ignore; }
|
||||
bool ignoreIconSize() { return m_IgnoreIconSize; }
|
||||
bool customCopyPasteHandler() { return m_customCopyPaste; }
|
||||
void setCustomCopyPasteHandler(bool custom) { m_customCopyPaste = custom; }
|
||||
QModelIndex currentHoverIndex() const { return m_currentHoverIndex; }
|
||||
void setItemDelegate(QAbstractItemDelegate *delegate);
|
||||
QAbstractItemDelegate *itemDelegate() const;
|
||||
@@ -121,6 +125,8 @@ public:
|
||||
}
|
||||
bool hasInternalExpansion(uint expansionID) { return m_Expansions.contains(expansionID); }
|
||||
void clearInternalExpansions() { m_Expansions.clear(); }
|
||||
virtual void copySelection();
|
||||
|
||||
signals:
|
||||
void leave(QEvent *e);
|
||||
void keyPress(QKeyEvent *e);
|
||||
@@ -147,6 +153,7 @@ private:
|
||||
bool m_VisibleBranches = true;
|
||||
bool m_VisibleGridLines = true;
|
||||
bool m_TooltipElidedItems = true;
|
||||
bool m_customCopyPaste = false;
|
||||
|
||||
QMap<uint, RDTreeViewExpansionState> m_Expansions;
|
||||
|
||||
|
||||
@@ -853,73 +853,6 @@ void RDTreeWidget::focusOutEvent(QFocusEvent *event)
|
||||
RDTreeView::focusOutEvent(event);
|
||||
}
|
||||
|
||||
void RDTreeWidget::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if(!m_customCopyPaste && e->matches(QKeySequence::Copy))
|
||||
{
|
||||
copySelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
RDTreeView::keyPressEvent(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
|
||||
|
||||
@@ -205,7 +205,6 @@ class RDTreeWidget : public RDTreeView
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool instantTooltips READ instantTooltips WRITE setInstantTooltips)
|
||||
Q_PROPERTY(bool customCopyPasteHandler READ customCopyPasteHandler WRITE setCustomCopyPasteHandler)
|
||||
public:
|
||||
explicit RDTreeWidget(QWidget *parent = 0);
|
||||
~RDTreeWidget();
|
||||
@@ -223,8 +222,6 @@ public:
|
||||
void setClearSelectionOnFocusLoss(bool clear) { m_clearSelectionOnFocusLoss = clear; }
|
||||
bool instantTooltips() { return m_instantTooltips; }
|
||||
void setInstantTooltips(bool instant) { m_instantTooltips = instant; }
|
||||
bool customCopyPasteHandler() { return m_customCopyPaste; }
|
||||
void setCustomCopyPasteHandler(bool custom) { m_customCopyPaste = custom; }
|
||||
RDTreeWidgetItem *invisibleRootItem() { return m_root; }
|
||||
void addTopLevelItem(RDTreeWidgetItem *item) { m_root->addChild(item); }
|
||||
RDTreeWidgetItem *topLevelItem(int index) const { return m_root->child(index); }
|
||||
@@ -254,8 +251,6 @@ public:
|
||||
void collapseAllItems(RDTreeWidgetItem *item);
|
||||
void scrollToItem(RDTreeWidgetItem *node);
|
||||
|
||||
void copySelection();
|
||||
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
@@ -275,7 +270,6 @@ private:
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
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;
|
||||
@@ -313,7 +307,6 @@ private:
|
||||
QVector<Qt::Alignment> m_alignments;
|
||||
|
||||
bool m_instantTooltips = false;
|
||||
bool m_customCopyPaste = false;
|
||||
int m_hoverColumn = -1;
|
||||
QIcon m_normalHoverIcon;
|
||||
QIcon m_activeHoverIcon;
|
||||
|
||||
Reference in New Issue
Block a user