Files
renderdoc/qrenderdoc/Widgets/PipelineFlowChart.cpp
T

305 lines
7.3 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2017 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "PipelineFlowChart.h"
#include <QMouseEvent>
#include <QPainter>
PipelineFlowChart::PipelineFlowChart(QWidget *parent) : QFrame(parent)
{
setMouseTracking(true);
}
PipelineFlowChart::~PipelineFlowChart()
{
}
void PipelineFlowChart::setStages(const QStringList &abbrevs, const QStringList &names)
{
m_StageNames = names;
m_StageAbbrevs = abbrevs;
m_StageFlows.reserve(m_StageNames.count());
m_StagesEnabled.reserve(m_StageNames.count());
for(int i = 0; i < m_StageNames.count(); i++)
{
m_StageFlows.push_back(true);
m_StagesEnabled.push_back(true);
}
update();
}
void PipelineFlowChart::setStageName(int index, const QString &abbrev, const QString &name)
{
if(index >= 0 && index < m_StageNames.count())
{
m_StageAbbrevs[index] = abbrev;
m_StageNames[index] = name;
}
update();
}
void PipelineFlowChart::setIsolatedStage(int index)
{
if(index >= 0 && index < m_StageFlows.count())
m_StageFlows[index] = false;
}
void PipelineFlowChart::setSelectedStage(int idx)
{
if(idx >= 0 && idx < m_StageNames.length())
{
m_SelectedStage = idx;
update();
emit stageSelected(idx);
}
}
int PipelineFlowChart::selectedStage()
{
return m_SelectedStage;
}
void PipelineFlowChart::setStagesEnabled(const QList<bool> &enabled)
{
for(int i = 0; i < enabled.count() && i < m_StagesEnabled.count(); i++)
m_StagesEnabled[i] = enabled[i];
update();
}
bool PipelineFlowChart::stageEnabled(int index)
{
return index >= 0 && index < m_StagesEnabled.count() && m_StagesEnabled[index];
}
int PipelineFlowChart::numGaps()
{
if(!m_StageNames.isEmpty())
return m_StageNames.count() - 1;
return 1;
}
int PipelineFlowChart::numItems()
{
if(!m_StageNames.isEmpty())
return m_StageNames.count();
return 2;
}
QRectF PipelineFlowChart::totalAreaRect()
{
QRectF rect = this->rect();
qreal m = 6 + BoxBorderWidth;
return rect.marginsRemoved(QMarginsF(m, m, m, m));
}
qreal PipelineFlowChart::boxMargin()
{
qreal margin = qMax(totalAreaRect().width(), totalAreaRect().height()) * BoxMarginFraction;
margin = qMax(MinBoxMargin, margin);
return margin;
}
QRectF PipelineFlowChart::boxRect(int i)
{
QRectF totalRect = totalAreaRect();
qreal boxeswidth = totalRect.width() - numGaps() * boxMargin();
qreal boxdim = qMin(totalRect.height(), boxeswidth / numItems());
boxdim = qMax(MinBoxDimension, boxdim);
qreal oblongwidth = qMax(0.0, (boxeswidth - boxdim * numItems()) / numItems());
QSizeF boxSize(boxdim + oblongwidth, boxdim);
QRectF ret(totalRect.x() + i * (boxSize.width() + boxMargin()),
totalRect.y() + totalRect.height() / 2 - boxSize.height() / 2, boxSize.width(),
boxSize.height());
return ret;
}
void PipelineFlowChart::drawArrow(QPainter &p, const QPen &pen, float headsize, float y, float left,
float right)
{
p.setPen(pen);
p.drawPolygon(QPolygonF({QPointF(right, y), QPointF(right - headsize, y - headsize),
QPointF(right - headsize, y + headsize)}));
QPen linePen(pen);
linePen.setWidthF(BoxBorderWidth);
p.setPen(linePen);
p.drawLine(QPointF(left, y), QPointF(right, y));
}
void PipelineFlowChart::paintEvent(QPaintEvent *e)
{
if(m_StageNames.empty())
return;
QPainter p(this);
p.fillRect(rect(), Qt::transparent);
p.setRenderHint(QPainter::Antialiasing, true);
const QRectF totalRect = totalAreaRect();
const QRectF box0Rect = boxRect(0);
const qreal radius = qMin(MaxBoxCornerRadius, box0Rect.height() * BoxCornerRadiusFraction);
const qreal arrowY = totalRect.y() + totalRect.height() / 2;
QColor base = palette().color(QPalette::Base);
QColor baseText = palette().color(QPalette::Text);
QColor inactiveWin = palette().color(QPalette::Inactive, QPalette::Dark);
QColor inactiveWinText = palette().color(QPalette::Inactive, QPalette::WindowText);
QColor tooltip = palette().color(QPalette::ToolTipBase);
QColor tooltipText = palette().color(QPalette::ToolTipText);
QPen pen(baseText);
QPen selectedPen(Qt::red);
int num = numGaps();
for(int i = 0; i < num; i++)
{
if(!m_StageFlows[i] || !m_StageFlows[i + 1])
continue;
float right = totalRect.x() + (i + 1) * (box0Rect.width() + boxMargin());
float left = right - boxMargin();
p.setBrush(baseText);
drawArrow(p, pen, ArrowHeadSize, arrowY, left, right);
}
num = numItems();
for(int i = 0; i < num; i++)
{
QRectF boxrect = boxRect(i);
QBrush backBrush(base);
QPen textPen(baseText);
QPen outlinePen = pen;
if(!stageEnabled(i))
{
backBrush.setColor(inactiveWin);
textPen.setColor(inactiveWinText);
}
if(i == m_HoverStage)
{
backBrush.setColor(tooltip);
textPen.setColor(tooltipText);
}
if(i == m_SelectedStage)
{
outlinePen = selectedPen;
}
outlinePen.setWidthF(BoxBorderWidth);
p.setPen(outlinePen);
p.setBrush(backBrush);
p.drawRoundedRect(boxrect, radius, radius);
QTextOption opts(Qt::AlignCenter);
opts.setWrapMode(QTextOption::NoWrap);
QString s = m_StageNames[i];
QRectF reqBox = p.boundingRect(QRectF(0, 0, 1, 1), m_StageNames[i], opts);
if(reqBox.width() + BoxLabelMargin > (float)boxrect.width())
s = m_StageAbbrevs[i];
p.setPen(textPen);
p.drawText(boxrect, s, opts);
}
}
void PipelineFlowChart::mouseMoveEvent(QMouseEvent *e)
{
int old = m_HoverStage;
m_HoverStage = -1;
const int num = numItems();
for(int i = 0; i < num; i++)
{
if(boxRect(i).contains(e->pos()))
{
m_HoverStage = i;
break;
}
}
if(m_HoverStage != old)
update();
}
void PipelineFlowChart::mousePressEvent(QMouseEvent *e)
{
if(e->button() == Qt::LeftButton)
{
const int num = numItems();
for(int i = 0; i < numItems(); i++)
{
if(boxRect(i).contains(e->pos()))
{
setSelectedStage(i);
break;
}
}
}
}
void PipelineFlowChart::leaveEvent(QEvent *e)
{
m_HoverStage = -1;
update();
}
void PipelineFlowChart::focusOutEvent(QFocusEvent *event)
{
m_HoverStage = -1;
update();
}