summaryrefslogtreecommitdiff
path: root/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/modelinglib/qmt/style/defaultstyleengine.cpp')
-rw-r--r--src/libs/modelinglib/qmt/style/defaultstyleengine.cpp587
1 files changed, 587 insertions, 0 deletions
diff --git a/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
new file mode 100644
index 0000000000..0971dc1ffb
--- /dev/null
+++ b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
@@ -0,0 +1,587 @@
+/***************************************************************************
+**
+** Copyright (C) 2015 Jochen Becher
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "defaultstyleengine.h"
+
+#include "defaultstyle.h"
+#include "objectvisuals.h"
+#include "styledobject.h"
+#include "styledrelation.h"
+
+#include "qmt/diagram/dclass.h"
+#include "qmt/diagram/dpackage.h"
+#include "qmt/diagram/dcomponent.h"
+#include "qmt/diagram/ditem.h"
+#include "qmt/diagram/dannotation.h"
+#include "qmt/infrastructure/qmtassert.h"
+
+#include <QSet>
+#include <QDebug>
+
+namespace {
+
+class DepthProperties
+{
+public:
+ DepthProperties() = default;
+ DepthProperties(qmt::DefaultStyleEngine::ElementType elementType,
+ qmt::DObject::VisualPrimaryRole visualPrimaryRole,
+ qmt::DObject::VisualSecondaryRole visualSecondaryRole)
+ : m_elementType(elementType),
+ m_visualPrimaryRole(visualPrimaryRole),
+ m_visualSecondaryRole(visualSecondaryRole)
+ {
+ }
+
+ qmt::DefaultStyleEngine::ElementType m_elementType = qmt::DefaultStyleEngine::TypeOther;
+ qmt::DObject::VisualPrimaryRole m_visualPrimaryRole = qmt::DObject::PrimaryRoleNormal;
+ qmt::DObject::VisualSecondaryRole m_visualSecondaryRole = qmt::DObject::SecondaryRoleNone;
+};
+
+} // namespace
+
+namespace qmt {
+
+// TODO use tuple instead of these 4 explicit key classes
+
+class ObjectStyleKey
+{
+public:
+ ObjectStyleKey() = default;
+
+ ObjectStyleKey(StyleEngine::ElementType elementType, const ObjectVisuals &objectVisuals)
+ : m_elementType(elementType),
+ m_objectVisuals(objectVisuals)
+ {
+ }
+
+ StyleEngine::ElementType m_elementType = StyleEngine::TypeOther;
+ ObjectVisuals m_objectVisuals;
+};
+
+uint qHash(const ObjectStyleKey &styleKey)
+{
+ return ::qHash(styleKey.m_elementType) ^ qHash(styleKey.m_objectVisuals);
+}
+
+bool operator==(const ObjectStyleKey &lhs, const ObjectStyleKey &rhs)
+{
+ return lhs.m_elementType == rhs.m_elementType && lhs.m_objectVisuals == rhs.m_objectVisuals;
+}
+
+class RelationStyleKey
+{
+public:
+ RelationStyleKey(StyleEngine::ElementType elementType = StyleEngine::TypeOther,
+ DObject::VisualPrimaryRole visualPrimaryRole = DObject::PrimaryRoleNormal)
+ : m_elementType(elementType),
+ m_visualPrimaryRole(visualPrimaryRole)
+ {
+ }
+
+ StyleEngine::ElementType m_elementType = StyleEngine::TypeOther;
+ DObject::VisualPrimaryRole m_visualPrimaryRole = DObject::PrimaryRoleNormal;
+};
+
+uint qHash(const RelationStyleKey &styleKey)
+{
+ return ::qHash(styleKey.m_elementType) ^ ::qHash(styleKey.m_visualPrimaryRole);
+}
+
+bool operator==(const RelationStyleKey &lhs, const RelationStyleKey &rhs)
+{
+ return lhs.m_elementType == rhs.m_elementType && lhs.m_visualPrimaryRole == rhs.m_visualPrimaryRole;
+}
+
+class AnnotationStyleKey
+{
+public:
+ AnnotationStyleKey(DAnnotation::VisualRole visualRole = DAnnotation::RoleNormal)
+ : m_visualRole(visualRole)
+ {
+ }
+
+ DAnnotation::VisualRole m_visualRole = DAnnotation::RoleNormal;
+};
+
+uint qHash(const AnnotationStyleKey &styleKey)
+{
+ return ::qHash(styleKey.m_visualRole);
+}
+
+bool operator==(const AnnotationStyleKey &lhs, const AnnotationStyleKey &rhs)
+{
+ return lhs.m_visualRole == rhs.m_visualRole;
+}
+
+// TODO remove class if no attributes needed even with future extensions
+class BoundaryStyleKey
+{
+};
+
+uint qHash(const BoundaryStyleKey &styleKey)
+{
+ Q_UNUSED(styleKey);
+
+ return 1;
+}
+
+bool operator==(const BoundaryStyleKey &lhs, const BoundaryStyleKey &rhs)
+{
+ Q_UNUSED(lhs);
+ Q_UNUSED(rhs);
+
+ return true;
+}
+
+DefaultStyleEngine::DefaultStyleEngine()
+{
+}
+
+DefaultStyleEngine::~DefaultStyleEngine()
+{
+ qDeleteAll(m_objectStyleMap);
+ qDeleteAll(m_relationStyleMap);
+ qDeleteAll(m_annotationStyleMap);
+ qDeleteAll(m_boundaryStyleMap);
+}
+
+const Style *DefaultStyleEngine::applyStyle(const Style *baseStyle, StyleEngine::ElementType elementType,
+ const StyleEngine::Parameters *parameters)
+{
+ switch (elementType) {
+ case TypeAnnotation:
+ return applyAnnotationStyle(baseStyle, DAnnotation::RoleNormal, parameters);
+ case TypeBoundary:
+ return applyBoundaryStyle(baseStyle, parameters);
+ case TypeRelation:
+ break;
+ case TypeClass:
+ case TypeComponent:
+ case TypeItem:
+ case TypePackage:
+ return applyObjectStyle(
+ baseStyle, elementType,
+ ObjectVisuals(DObject::PrimaryRoleNormal, DObject::SecondaryRoleNone, false, QColor(), 0),
+ parameters);
+ case TypeOther:
+ break;
+ }
+ return baseStyle;
+}
+
+const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, StyleEngine::ElementType elementType,
+ const ObjectVisuals &objectVisuals,
+ const StyleEngine::Parameters *parameters)
+{
+ ObjectStyleKey key(elementType, objectVisuals);
+ const Style *derivedStyle = m_objectStyleMap.value(key);
+ if (!derivedStyle) {
+ int lineWidth = 1;
+
+ QColor fillColor = DefaultStyleEngine::fillColor(elementType, objectVisuals);
+ QColor lineColor = DefaultStyleEngine::lineColor(elementType, objectVisuals);
+ QColor textColor = DefaultStyleEngine::textColor(elementType, objectVisuals);
+
+ QFont normalFont = baseStyle->normalFont();
+ QFont headerFont = baseStyle->normalFont();
+ if (objectVisuals.isEmphasized()) {
+ lineWidth = 2;
+ headerFont.setBold(true);
+ }
+
+ auto style = new Style(baseStyle->type());
+ QPen linePen = baseStyle->linePen();
+ linePen.setColor(lineColor);
+ linePen.setWidth(lineWidth);
+ style->setLinePen(linePen);
+ style->setInnerLinePen(linePen);
+ style->setOuterLinePen(linePen);
+ style->setExtraLinePen(linePen);
+ style->setTextBrush(QBrush(textColor));
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline) {
+ style->setFillBrush(QBrush(Qt::white));
+ } else {
+ if (!parameters->suppressGradients()) {
+ QLinearGradient fillGradient(0.0, 0.0, 0.0, 1.0);
+ fillGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ fillGradient.setColorAt(0.0, fillColor.lighter(110));
+ fillGradient.setColorAt(1.0, fillColor.darker(110));
+ style->setFillBrush(QBrush(fillGradient));
+ } else {
+ style->setFillBrush(QBrush(fillColor));
+ }
+ }
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline)
+ style->setExtraFillBrush(QBrush(Qt::white));
+ else
+ style->setExtraFillBrush(QBrush(fillColor.darker(120)));
+ style->setNormalFont(normalFont);
+ style->setSmallFont(baseStyle->smallFont());
+ style->setHeaderFont(headerFont);
+ m_objectStyleMap.insert(key, style);
+ derivedStyle = style;
+ }
+
+ return derivedStyle;
+}
+
+const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const StyledObject &styledObject,
+ const Parameters *parameters)
+{
+ ElementType elementType = objectType(styledObject.object());
+
+ // find colliding elements which best match visual appearance of styled object
+ DObject::VisualPrimaryRole styledVisualPrimaryRole = styledObject.objectVisuals().visualPrimaryRole();
+ DObject::VisualSecondaryRole styledVisualSecondaryRole = styledObject.objectVisuals().visualSecondaryRole();
+ QHash<int, DepthProperties> depths;
+ foreach (const DObject *collidingObject, styledObject.collidingObjects()) {
+ int collidingDepth = collidingObject->depth();
+ if (collidingDepth < styledObject.object()->depth()) {
+ ElementType collidingElementType = objectType(collidingObject);
+ DObject::VisualPrimaryRole collidingVisualPrimaryRole = collidingObject->visualPrimaryRole();
+ DObject::VisualSecondaryRole collidingVisualSecondaryRole = collidingObject->visualSecondaryRole();
+ if (!depths.contains(collidingDepth)) {
+ depths.insert(collidingDepth, DepthProperties(collidingElementType, collidingVisualPrimaryRole,
+ collidingVisualSecondaryRole));
+ } else {
+ bool updateProperties = false;
+ DepthProperties properties = depths.value(collidingDepth);
+ if (properties.m_elementType != elementType && collidingElementType == elementType) {
+ properties.m_elementType = collidingElementType;
+ properties.m_visualPrimaryRole = collidingVisualPrimaryRole;
+ properties.m_visualSecondaryRole = collidingVisualSecondaryRole;
+ updateProperties = true;
+ } else if (properties.m_elementType == elementType && collidingElementType == elementType) {
+ if ((properties.m_visualPrimaryRole != styledVisualPrimaryRole
+ || properties.m_visualSecondaryRole != styledVisualSecondaryRole)
+ && collidingVisualPrimaryRole == styledVisualPrimaryRole
+ && collidingVisualSecondaryRole == styledVisualSecondaryRole) {
+ properties.m_visualPrimaryRole = collidingVisualPrimaryRole;
+ properties.m_visualSecondaryRole = collidingVisualSecondaryRole;
+ updateProperties = true;
+ }
+ }
+ if (updateProperties)
+ depths.insert(collidingDepth, properties);
+ }
+ }
+ }
+ int depth = 0;
+ if (!depths.isEmpty()) {
+ QList<int> keys = depths.keys();
+ qSort(keys);
+ foreach (int d, keys) {
+ DepthProperties properties = depths.value(d);
+ if (properties.m_elementType == elementType
+ && areStackingRoles(properties.m_visualPrimaryRole, properties.m_visualSecondaryRole,
+ styledVisualPrimaryRole, styledVisualSecondaryRole)) {
+ ++depth;
+ } else {
+ depth = 0;
+ }
+ }
+ }
+
+ return applyObjectStyle(baseStyle, elementType,
+ ObjectVisuals(styledVisualPrimaryRole,
+ styledVisualSecondaryRole,
+ styledObject.objectVisuals().isEmphasized(),
+ styledObject.objectVisuals().baseColor(),
+ depth),
+ parameters);
+}
+
+const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, const StyledRelation &styledRelation,
+ const Parameters *parameters)
+{
+ Q_UNUSED(parameters);
+
+ ElementType elementType = objectType(styledRelation.endA());
+ RelationStyleKey key(elementType, styledRelation.endA() ? styledRelation.endA()->visualPrimaryRole() : DObject::PrimaryRoleNormal);
+ const Style *derivedStyle = m_relationStyleMap.value(key);
+ if (!derivedStyle) {
+ auto style = new Style(baseStyle->type());
+
+ const DObject *object = styledRelation.endA();
+ ObjectVisuals objectVisuals(object ? object->visualPrimaryRole() : DObject::PrimaryRoleNormal,
+ object ? object->visualSecondaryRole() : DObject::SecondaryRoleNone,
+ object ? object->isVisualEmphasized() : false,
+ Qt::black, // TODO STyledRelation should get an EndAObjectVisuals
+ object ? object->depth() : 0);
+ QColor lineColor = DefaultStyleEngine::lineColor(objectType(object), objectVisuals);
+ QColor fillColor = lineColor;
+
+ QPen linePen = baseStyle->linePen();
+ linePen.setWidth(1);
+ linePen.setColor(lineColor);
+ style->setLinePen(linePen);
+ QBrush textBrush = baseStyle->textBrush();
+ textBrush.setColor(QColor("black"));
+ style->setTextBrush(textBrush);
+ QBrush brush = baseStyle->fillBrush();
+ brush.setColor(fillColor);
+ brush.setStyle(Qt::SolidPattern);
+ style->setFillBrush(brush);
+ style->setNormalFont(baseStyle->normalFont());
+ style->setSmallFont(baseStyle->smallFont());
+ style->setHeaderFont(baseStyle->headerFont());
+ m_relationStyleMap.insert(key, style);
+ derivedStyle = style;
+ }
+ return derivedStyle;
+}
+
+const Style *DefaultStyleEngine::applyAnnotationStyle(const Style *baseStyle, const DAnnotation *annotation,
+ const Parameters *parameters)
+{
+ DAnnotation::VisualRole visualRole = annotation ? annotation->visualRole() : DAnnotation::RoleNormal;
+ return applyAnnotationStyle(baseStyle, visualRole, parameters);
+}
+
+const Style *DefaultStyleEngine::applyBoundaryStyle(const Style *baseStyle, const DBoundary *boundary,
+ const Parameters *parameters)
+{
+ Q_UNUSED(boundary);
+
+ return applyBoundaryStyle(baseStyle, parameters);
+}
+
+const Style *DefaultStyleEngine::applyAnnotationStyle(const Style *baseStyle, DAnnotation::VisualRole visualRole,
+ const StyleEngine::Parameters *parameters)
+{
+ Q_UNUSED(parameters);
+
+ AnnotationStyleKey key(visualRole);
+ const Style *derivedStyle = m_annotationStyleMap.value(key);
+ if (!derivedStyle) {
+ auto style = new Style(baseStyle->type());
+ QFont normalFont;
+ QBrush textBrush = baseStyle->textBrush();
+ switch (visualRole) {
+ case DAnnotation::RoleNormal:
+ normalFont = baseStyle->normalFont();
+ break;
+ case DAnnotation::RoleTitle:
+ normalFont = baseStyle->headerFont();
+ break;
+ case DAnnotation::RoleSubtitle:
+ normalFont = baseStyle->normalFont();
+ normalFont.setItalic(true);
+ break;
+ case DAnnotation::RoleEmphasized:
+ normalFont = baseStyle->normalFont();
+ normalFont.setBold(true);
+ break;
+ case DAnnotation::RoleSoften:
+ normalFont = baseStyle->normalFont();
+ textBrush.setColor(Qt::gray);
+ break;
+ case DAnnotation::RoleFootnote:
+ normalFont = baseStyle->smallFont();
+ break;
+ }
+ style->setNormalFont(normalFont);
+ style->setTextBrush(textBrush);
+ m_annotationStyleMap.insert(key, style);
+ derivedStyle = style;
+ }
+ return derivedStyle;
+}
+
+const Style *DefaultStyleEngine::applyBoundaryStyle(const Style *baseStyle, const StyleEngine::Parameters *parameters)
+{
+ Q_UNUSED(parameters);
+
+ BoundaryStyleKey key;
+ const Style *derivedStyle = m_boundaryStyleMap.value(key);
+ if (!derivedStyle) {
+ auto style = new Style(baseStyle->type());
+ style->setNormalFont(baseStyle->normalFont());
+ style->setTextBrush(baseStyle->textBrush());
+ m_boundaryStyleMap.insert(key, style);
+ derivedStyle = style;
+ }
+ return derivedStyle;
+}
+
+DefaultStyleEngine::ElementType DefaultStyleEngine::objectType(const DObject *object)
+{
+ ElementType elementType;
+ if (dynamic_cast<const DPackage *>(object))
+ elementType = TypePackage;
+ else if (dynamic_cast<const DComponent *>(object))
+ elementType = TypeComponent;
+ else if (dynamic_cast<const DClass *>(object))
+ elementType = TypeClass;
+ else if (dynamic_cast<const DItem *>(object))
+ elementType = TypeItem;
+ else
+ elementType = TypeOther;
+ return elementType;
+}
+
+bool DefaultStyleEngine::areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole,
+ DObject::VisualSecondaryRole rhsSecondaryRole,
+ DObject::VisualPrimaryRole lhsPrimaryRole,
+ DObject::VisualSecondaryRole lhsSecondaryRols)
+{
+ switch (rhsSecondaryRole) {
+ case DObject::SecondaryRoleNone:
+ case DObject::SecondaryRoleLighter:
+ case DObject::SecondaryRoleDarker:
+ switch (lhsSecondaryRols) {
+ case DObject::SecondaryRoleNone:
+ case DObject::SecondaryRoleLighter:
+ case DObject::SecondaryRoleDarker:
+ return lhsPrimaryRole == rhsPrimaryRole;
+ break;
+ case DObject::SecondaryRoleSoften:
+ case DObject::SecondaryRoleOutline:
+ return false;
+ }
+ case DObject::SecondaryRoleSoften:
+ case DObject::SecondaryRoleOutline:
+ return false;
+ }
+ return true;
+}
+
+QColor DefaultStyleEngine::baseColor(ElementType elementType, ObjectVisuals objectVisuals)
+{
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline)
+ return QColor("#FFFFFF");
+
+ QColor baseColor;
+
+ if (objectVisuals.visualPrimaryRole() == DObject::PrimaryRoleNormal) {
+ if (objectVisuals.baseColor().isValid()) {
+ baseColor = objectVisuals.baseColor();
+ } else {
+ switch (elementType) {
+ case TypePackage:
+ baseColor = QColor("#7C98AD");
+ break;
+ case TypeComponent:
+ baseColor = QColor("#A0A891");
+ break;
+ case TypeClass:
+ baseColor = QColor("#E5A858");
+ break;
+ case TypeItem:
+ baseColor = QColor("#B995C6");
+ break;
+ default:
+ baseColor = QColor("#BF7D65");
+ break;
+ }
+ }
+ } else {
+ static QColor customColors[] = {
+ QColor("#EE8E99").darker(110), // ROLE_CUSTOM1,
+ QColor("#80AF47").lighter(130), // ROLE_CUSTOM2,
+ QColor("#FFA15B").lighter(100), // ROLE_CUSTOM3,
+ QColor("#55C4CF").lighter(120), // ROLE_CUSTOM4,
+ QColor("#FFE14B") // ROLE_CUSTOM5,
+ };
+
+ int index = static_cast<int>(objectVisuals.visualPrimaryRole()) - static_cast<int>(DObject::PrimaryRoleCustom1);
+ QMT_CHECK(index >= 0 && index <= 4);
+ baseColor = customColors[index];
+ }
+
+ switch (objectVisuals.visualSecondaryRole()) {
+ case DObject::SecondaryRoleNone:
+ break;
+ case DObject::SecondaryRoleLighter:
+ baseColor = baseColor.lighter(110);
+ break;
+ case DObject::SecondaryRoleDarker:
+ baseColor = baseColor.darker(120);
+ break;
+ case DObject::SecondaryRoleSoften:
+ baseColor = baseColor.lighter(300);
+ break;
+ case DObject::SecondaryRoleOutline:
+ QMT_CHECK(false);
+ break;
+ }
+
+ return baseColor;
+}
+
+QColor DefaultStyleEngine::lineColor(ElementType elementType, const ObjectVisuals &objectVisuals)
+{
+ QColor lineColor;
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline)
+ lineColor = Qt::black;
+ else if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleSoften)
+ lineColor = Qt::gray;
+ else
+ lineColor = baseColor(elementType, objectVisuals).darker(200).lighter(150).darker(100 + objectVisuals.depth() * 10);
+ return lineColor;
+}
+
+QColor DefaultStyleEngine::fillColor(ElementType elementType, const ObjectVisuals &objectVisuals)
+{
+ QColor fillColor;
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline)
+ fillColor = Qt::white;
+ else
+ fillColor = baseColor(elementType, objectVisuals).lighter(150).darker(100 + objectVisuals.depth() * 10);
+ return fillColor;
+}
+
+QColor DefaultStyleEngine::textColor(const DObject *object, int depth)
+{
+ Q_UNUSED(depth);
+
+ QColor textColor;
+ DObject::VisualPrimaryRole visualRole = object ? object->visualPrimaryRole() : DObject::PrimaryRoleNormal;
+ if (visualRole == DObject::DeprecatedPrimaryRoleSoften)
+ textColor = Qt::gray;
+ else
+ textColor = Qt::black;
+ return textColor;
+}
+
+QColor DefaultStyleEngine::textColor(ElementType elementType, const ObjectVisuals &objectVisuals)
+{
+ Q_UNUSED(elementType);
+
+ QColor textColor;
+ if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleSoften)
+ textColor = Qt::gray;
+ else
+ textColor = Qt::black;
+ return textColor;
+}
+
+} // namespace qmt