QmlDesigner: Add radius to transition item

* Add support for rounded corners on transition item
* Cleanup connection drawing

Task-number: QDS-1788
Change-Id: I53698687b911ad9a43309c53bb599b7f0212f90d
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Henning Gruendl
2020-04-29 15:11:02 +02:00
committed by Thomas Hartmann
parent 1d5b54f1c8
commit 06c0f74fec
3 changed files with 178 additions and 132 deletions

View File

@@ -793,10 +793,10 @@ QPointF FormEditorTransitionItem::instancePosition() const
static bool verticalOverlap(const QRectF &from, const QRectF &to) static bool verticalOverlap(const QRectF &from, const QRectF &to)
{ {
if (from.top() < to.bottom() && (from.top() + from.height()) > to.top()) if (from.top() < to.bottom() && from.bottom() > to.top())
return true; return true;
if (to.top() < from.bottom() && (to.top() + to.height()) > from.top()) if (to.top() < from.bottom() && to.bottom() > from.top())
return true; return true;
return false; return false;
@@ -805,25 +805,77 @@ static bool verticalOverlap(const QRectF &from, const QRectF &to)
static bool horizontalOverlap(const QRectF &from, const QRectF &to) static bool horizontalOverlap(const QRectF &from, const QRectF &to)
{ {
if (from.left() < to.right() && (from.left() + from.width()) > to.left()) if (from.left() < to.right() && from.right() > to.left())
return true; return true;
if (to.left() < from.right() && (to.left() + to.width()) > from.left()) if (to.left() < from.right() && to.right() > from.left())
return true; return true;
return false; return false;
} }
static void drawArrow(QPainter *painter,
const QLineF &line,
int arrowLength,
int arrowWidth)
{
const QPointF peakP(0, 0);
const QPointF leftP(-arrowLength, -arrowWidth * 0.5);
const QPointF rightP(-arrowLength, arrowWidth * 0.5);
painter->save();
painter->translate(line.p2());
painter->rotate(-line.angle());
painter->drawLine(leftP, peakP);
painter->drawLine(rightP, peakP);
painter->restore();
}
static void drawRoundedCorner(QPainter *painter,
const QPointF &s,
const QPointF &m,
const QPointF &e,
int radius)
{
const QVector2D sm(m - s);
const QVector2D me(e - m);
const float smLength = sm.length();
const float meLength = me.length();
const int actualRadius = qMin(radius, static_cast<int>(qMin(smLength, meLength)));
const QVector2D smNorm = sm.normalized();
const QVector2D meNorm = me.normalized();
const QPointF arcStartP = s + (smNorm * (smLength - actualRadius)).toPointF();
QRectF rect(m, QSizeF(actualRadius * 2, actualRadius * 2));
painter->drawLine(s, arcStartP);
if ((smNorm.y() < 0 && meNorm.x() > 0) || (smNorm.x() < 0 && meNorm.y() > 0)) {
rect.moveTopLeft(m);
painter->drawArc(rect, 90 * 16, 90 * 16);
} else if ((smNorm.y() > 0 && meNorm.x() > 0) || (smNorm.x() < 0 && meNorm.y() < 0)) {
rect.moveBottomLeft(m);
painter->drawArc(rect, 180 * 16, 90 * 16);
} else if ((smNorm.x() > 0 && meNorm.y() > 0) || (smNorm.y() < 0 && meNorm.x() < 0)) {
rect.moveTopRight(m);
painter->drawArc(rect, 0 * 16, 90 * 16);
} else if ((smNorm.y() > 0 && meNorm.x() < 0) || (smNorm.x() > 0 && meNorm.y() < 0)) {
rect.moveBottomRight(m);
painter->drawArc(rect, 270 * 16, 90 * 16);
}
const QPointF arcEndP = e - (meNorm * (meLength - actualRadius)).toPointF();
painter->drawLine(arcEndP, e);
}
static void paintConnection(QPainter *painter, static void paintConnection(QPainter *painter,
const QRectF &from, const QRectF &from,
const QRectF &to, const QRectF &to,
qreal width, const ConnectionStyle &style)
qreal adjustedWidth,
const QColor &color,
bool dash,
int startOffset,
int endOffset,
int breakOffset)
{ {
painter->save(); painter->save();
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
@@ -832,23 +884,22 @@ static void paintConnection(QPainter *painter,
pen.setCosmetic(true); pen.setCosmetic(true);
pen.setJoinStyle(Qt::MiterJoin); pen.setJoinStyle(Qt::MiterJoin);
pen.setCapStyle(Qt::RoundCap); pen.setCapStyle(Qt::RoundCap);
pen.setColor(style.color);
pen.setColor(color); if (style.dash)
if (dash)
pen.setStyle(Qt::DashLine); pen.setStyle(Qt::DashLine);
else else
pen.setStyle(Qt::SolidLine); pen.setStyle(Qt::SolidLine);
pen.setWidthF(width); pen.setWidthF(style.width);
painter->setPen(pen); painter->setPen(pen);
//const bool forceVertical = false; //const bool forceVertical = false;
//const bool forceHorizontal = false; //const bool forceHorizontal = false;
const int padding = 2 * width + 2 * adjustedWidth; const int padding = 2 * style.width + 2 * style.adjustedWidth;
const int arrowLength = 4 * adjustedWidth; const int arrowLength = 4 * style.adjustedWidth;
const int arrowWidth = 8 * adjustedWidth; const int arrowWidth = 8 * style.adjustedWidth;
const bool boolExitRight = from.right() < to.center().x(); const bool boolExitRight = from.right() < to.center().x();
const bool boolExitBottom = from.bottom() < to.center().y(); const bool boolExitBottom = from.bottom() < to.center().y();
@@ -860,9 +911,10 @@ static void paintConnection(QPainter *painter,
horizontalFirst = false; horizontalFirst = false;
*/ */
const qreal middleFactor = breakOffset / 100.0; const qreal middleFactor = style.breakOffset / 100.0;
QPointF startP; QPointF startP;
QLineF lastSegment;
bool extraLine = false; bool extraLine = false;
@@ -886,130 +938,97 @@ static void paintConnection(QPainter *painter,
} }
if (horizontalFirst) { if (horizontalFirst) {
const qreal startY = from.center().y() + startOffset; const qreal startX = boolExitRight ? from.right() + padding : from.x() - padding;
qreal startX = from.x() - padding; const qreal startY = from.center().y() + style.outOffset;
if (boolExitRight)
startX = from.right() + padding;
startP = QPointF(startX, startY); startP = QPointF(startX, startY);
qreal endY = to.top() - padding; const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding;
if (from.bottom() > to.y())
endY = to.bottom() + padding;
if (!extraLine) { if (!extraLine) {
const qreal endX = to.center().x() + style.inOffset;
const qreal endX = to.center().x() + endOffset;
const QPointF midP(endX, startY); const QPointF midP(endX, startY);
const QPointF endP(endX, endY); const QPointF endP(endX, endY);
painter->drawLine(startP, midP); if (style.radius == 0) {
painter->drawLine(midP, endP); painter->drawLine(startP, midP);
painter->drawLine(midP, endP);
} else {
drawRoundedCorner(painter, startP, midP, endP, style.radius);
}
int flip = 1; lastSegment = QLineF(midP, endP);
if (midP.y() < endP.y())
flip = -1;
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP);
painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP);
} else { } else {
const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding;
qreal endX = to.left() - padding; const qreal midX = startX * middleFactor + endX * (1 - middleFactor);
if (from.right() > to.x())
endX = to.right() + padding;
const qreal midX = startX * middleFactor + endX * (1-middleFactor);
const QPointF midP(midX, startY); const QPointF midP(midX, startY);
const QPointF midP2(midX, to.center().y() + endOffset); const QPointF midP2(midX, to.center().y() + style.inOffset);
const QPointF endP(endX, to.center().y() + endOffset); const QPointF endP(endX, to.center().y() + style.inOffset);
painter->drawLine(startP, midP);
painter->drawLine(midP, midP2);
painter->drawLine(midP2, endP);
int flip = 1; if (style.radius == 0) {
painter->drawLine(startP, midP);
painter->drawLine(midP, midP2);
painter->drawLine(midP2, endP);
} else {
const QLineF breakLine(midP, midP2);
drawRoundedCorner(painter, startP, midP, breakLine.center(), style.radius);
drawRoundedCorner(painter, breakLine.center(), midP2, endP, style.radius);
}
if (midP2.x() < endP.x()) lastSegment = QLineF(midP2, endP);
flip = -1;
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP);
painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP);
} }
} else { } else {
const qreal startX = from.center().x() + startOffset; const qreal startX = from.center().x() + style.outOffset;
const qreal startY = boolExitBottom ? from.bottom() + padding : from.top() - padding;
qreal startY = from.top() - padding;
if (boolExitBottom)
startY = from.bottom() + padding;
startP = QPointF(startX, startY); startP = QPointF(startX, startY);
qreal endX = to.left() - padding;
if (from.right() > to.x()) const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding;
endX = to.right() + padding;
if (!extraLine) { if (!extraLine) {
const qreal endY = to.center().y() + endOffset; const qreal endY = to.center().y() + style.inOffset;
const QPointF midP(startX, endY); const QPointF midP(startX, endY);
const QPointF endP(endX, endY); const QPointF endP(endX, endY);
painter->drawLine(startP, midP); if (style.radius == 0) {
painter->drawLine(midP, endP); painter->drawLine(startP, midP);
painter->drawLine(midP, endP);
} else {
drawRoundedCorner(painter, startP, midP, endP, style.radius);
}
int flip = 1; lastSegment = QLineF(midP, endP);
if (midP.x() < endP.x())
flip = -1;
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP);
painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP);
} else { } else {
const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding;
qreal endY = to.top() - padding; const qreal midY = startY * middleFactor + endY * (1 - middleFactor);
if (from.bottom() > to.y())
endY = to.bottom() + padding;
const qreal midY = startY * middleFactor + endY * (1-middleFactor);
const QPointF midP(startX, midY); const QPointF midP(startX, midY);
const QPointF midP2(to.center().x() + endOffset, midY); const QPointF midP2(to.center().x() + style.inOffset, midY);
const QPointF endP(to.center().x() + endOffset, endY); const QPointF endP(to.center().x() + style.inOffset, endY);
painter->drawLine(startP, midP); if (style.radius == 0) {
painter->drawLine(midP, midP2); painter->drawLine(startP, midP);
painter->drawLine(midP2, endP); painter->drawLine(midP, midP2);
painter->drawLine(midP2, endP);
} else {
const QLineF breakLine(midP, midP2);
drawRoundedCorner(painter, startP, midP, breakLine.center(), style.radius);
drawRoundedCorner(painter, breakLine.center(), midP2, endP, style.radius);
}
int flip = 1; lastSegment = QLineF(midP2, endP);
if (midP2.y() < endP.y())
flip = -1;
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP);
painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP);
} }
} }
pen.setWidthF(width); pen.setWidthF(style.width);
pen.setStyle(Qt::SolidLine); pen.setStyle(Qt::SolidLine);
painter->setPen(pen); painter->setPen(pen);
drawArrow(painter, lastSegment, arrowLength, arrowWidth);
painter->setBrush(Qt::white); painter->setBrush(Qt::white);
painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3); painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3);
painter->restore(); painter->restore();
} }
@@ -1066,57 +1085,67 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi
toRect.translate(-pos()); toRect.translate(-pos());
fromRect.translate(-pos()); fromRect.translate(-pos());
qreal width = 2; ConnectionStyle style;
style.width = 2;
const qreal scaleFactor = viewportTransform().m11(); const qreal scaleFactor = viewportTransform().m11();
if (qmlItemNode().modelNode().hasAuxiliaryData("width")) if (qmlItemNode().modelNode().hasAuxiliaryData("width"))
width = qmlItemNode().modelNode().auxiliaryData("width").toInt(); style.width = qmlItemNode().modelNode().auxiliaryData("width").toInt();
qreal adjustedWidth = width / scaleFactor; style.adjustedWidth = style.width / scaleFactor;
if (qmlItemNode().modelNode().isSelected()) if (qmlItemNode().modelNode().isSelected())
width += 2; style.width += 2;
if (m_hitTest) if (m_hitTest)
width *= 8; style.width *= 8;
QColor color = "#e71919"; style.color = "#e71919";
if (resolved.isStartLine) if (resolved.isStartLine)
color = "blue"; style.color = "blue";
if (resolved.isWildcardLine) if (resolved.isWildcardLine)
color = "green"; style.color = "green";
bool dash = false;
if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionColor")) if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionColor"))
color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>(); style.color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>();
if (qmlItemNode().modelNode().hasAuxiliaryData("color")) if (qmlItemNode().modelNode().hasAuxiliaryData("color"))
color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>(); style.color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>();
style.dash = false;
if (qmlItemNode().modelNode().hasAuxiliaryData("dash")) if (qmlItemNode().modelNode().hasAuxiliaryData("dash"))
dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool(); style.dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool();
int outOffset = 0; style.outOffset = 0;
int inOffset = 0; style.inOffset = 0;
if (qmlItemNode().modelNode().hasAuxiliaryData("outOffset")) if (qmlItemNode().modelNode().hasAuxiliaryData("outOffset"))
outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt(); style.outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt();
if (qmlItemNode().modelNode().hasAuxiliaryData("inOffset")) if (qmlItemNode().modelNode().hasAuxiliaryData("inOffset"))
inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt(); style.inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt();
int breakOffset = 50; style.breakOffset = 50;
if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint")) if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint"))
breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt(); style.breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt();
style.radius = 8;
if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionRadius"))
style.radius = qmlItemNode().rootModelNode().auxiliaryData("transitionRadius").toInt();
if (qmlItemNode().modelNode().hasAuxiliaryData("radius"))
style.radius = qmlItemNode().modelNode().auxiliaryData("radius").toInt();
if (resolved.isStartLine) if (resolved.isStartLine)
fromRect.translate(0, inOffset); fromRect.translate(0, style.outOffset);
paintConnection(painter, fromRect, toRect, width, adjustedWidth ,color, dash, outOffset, inOffset, breakOffset); paintConnection(painter, fromRect, toRect, style);
if (resolved.isStartLine) { if (resolved.isStartLine) {
@@ -1124,7 +1153,7 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi
QPen pen; QPen pen;
pen.setCosmetic(true); pen.setCosmetic(true);
pen.setColor(color); pen.setColor(style.color);
painter->setPen(pen); painter->setPen(pen);
const int iconAdjust = 48; const int iconAdjust = 48;
@@ -1134,7 +1163,7 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi
const int x = fromRect.topRight().x() - offset; const int x = fromRect.topRight().x() - offset;
const int y = fromRect.topRight().y(); const int y = fromRect.topRight().y();
painter->drawRoundedRect(x, y , size - 10, size, size / 2, iconSize / 2); painter->drawRoundedRect(x, y , size - 10, size, size / 2, iconSize / 2);
drawIcon(painter, x + iconAdjust / 2, y + iconAdjust / 2, icon, iconSize, iconSize, color); drawIcon(painter, x + iconAdjust / 2, y + iconAdjust / 2, icon, iconSize, iconSize, style.color);
} }
painter->restore(); painter->restore();

View File

@@ -47,6 +47,19 @@ namespace Internal {
class MoveController; class MoveController;
} }
class ConnectionStyle
{
public:
qreal width;
qreal adjustedWidth;
QColor color;
bool dash;
int outOffset;
int inOffset;
int breakOffset;
int radius;
};
class QMLDESIGNERCORE_EXPORT FormEditorItem : public QGraphicsItem class QMLDESIGNERCORE_EXPORT FormEditorItem : public QGraphicsItem
{ {
friend class QmlDesigner::FormEditorScene; friend class QmlDesigner::FormEditorScene;

View File

@@ -167,6 +167,10 @@ QVariant properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
return 0; return 0;
else if (propertyName == "breakPoint") else if (propertyName == "breakPoint")
return 50; return 50;
else if (propertyName == "transitionRadius")
return 8;
else if (propertyName == "radius")
return 8;
else if (propertyName == "customId") else if (propertyName == "customId")
return QString(); return QString();
else if (propertyName == "joinConnection") else if (propertyName == "joinConnection")
@@ -236,7 +240,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
propertyNames.append("customId"); propertyNames.append("customId");
if (itemNode.isFlowTransition()) { if (itemNode.isFlowTransition()) {
propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint"}); propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint", "radius"});
} else if (itemNode.isFlowItem()) { } else if (itemNode.isFlowItem()) {
propertyNames.append({"color", "width", "inOffset", "outOffset", "joinConnection"}); propertyNames.append({"color", "width", "inOffset", "outOffset", "joinConnection"});
} else if (itemNode.isFlowActionArea()) { } else if (itemNode.isFlowActionArea()) {
@@ -246,7 +250,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
} else if (itemNode.isFlowWildcard()) { } else if (itemNode.isFlowWildcard()) {
propertyNames.append({"color", "width", "fillColor", "dash"}); propertyNames.append({"color", "width", "fillColor", "dash"});
} else if (itemNode.isFlowView()) { } else if (itemNode.isFlowView()) {
propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor" }); propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor", "transitionRadius"});
} }
for (const PropertyName &propertyName : propertyNames) { for (const PropertyName &propertyName : propertyNames) {