/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://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 https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "formeditoritem.h" #include "formeditorscene.h" #include #include #include #include #include #include #include #include #include namespace QmlDesigner { FormEditorScene *FormEditorItem::scene() const { return qobject_cast(QGraphicsItem::scene()); } FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) : QGraphicsItem(scene->formLayerItem()), m_snappingLineCreator(this), m_qmlItemNode(qmlItemNode), m_borderWidth(1.0), m_highlightBoundingRect(false), m_blurContent(false), m_isContentVisible(true), m_isFormEditorVisible(true) { setCacheMode(QGraphicsItem::NoCache); setup(); } void FormEditorItem::setup() { setAcceptedMouseButtons(Qt::NoButton); if (qmlItemNode().hasInstanceParent()) { setParentItem(scene()->itemForQmlItemNode(qmlItemNode().instanceParent().toQmlItemNode())); setOpacity(qmlItemNode().instanceValue("opacity").toDouble()); } setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode().instanceValue("clip").toBool()); if (NodeHints::fromModelNode(qmlItemNode()).forceClip()) setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); if (QGraphicsItem::parentItem() == scene()->formLayerItem()) m_borderWidth = 0.0; setContentVisible(qmlItemNode().instanceValue("visible").toBool()); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, true); updateGeometry(); updateVisibilty(); } QRectF FormEditorItem::boundingRect() const { return m_boundingRect.adjusted(-2, -2, 2, 2); } QPainterPath FormEditorItem::shape() const { QPainterPath painterPath; painterPath.addRect(m_selectionBoundingRect); return painterPath; } bool FormEditorItem::contains(const QPointF &point) const { return m_selectionBoundingRect.contains(point); } void FormEditorItem::updateGeometry() { prepareGeometryChange(); m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect(); m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect); setTransform(qmlItemNode().instanceTransformWithContentTransform()); //the property for zValue is called z in QGraphicsObject if (qmlItemNode().instanceValue("z").isValid() && !qmlItemNode().isRootModelNode()) setZValue(qmlItemNode().instanceValue("z").toDouble()); } void FormEditorItem::updateVisibilty() { } FormEditorView *FormEditorItem::formEditorView() const { return scene()->editorView(); } void FormEditorItem::setHighlightBoundingRect(bool highlight) { if (m_highlightBoundingRect != highlight) { m_highlightBoundingRect = highlight; update(); } } void FormEditorItem::blurContent(bool blurContent) { if (!scene()) return; if (m_blurContent != blurContent) { m_blurContent = blurContent; update(); } } void FormEditorItem::setContentVisible(bool visible) { if (visible == m_isContentVisible) return; m_isContentVisible = visible; update(); } bool FormEditorItem::isContentVisible() const { if (parentItem()) return parentItem()->isContentVisible() && m_isContentVisible; return m_isContentVisible; } bool FormEditorItem::isFormEditorVisible() const { return m_isFormEditorVisible; } void FormEditorItem::setFormEditorVisible(bool isVisible) { m_isFormEditorVisible = isVisible; setVisible(isVisible); } QPointF FormEditorItem::center() const { return mapToScene(qmlItemNode().instanceBoundingRect().center()); } qreal FormEditorItem::selectionWeigth(const QPointF &point, int iteration) { if (!qmlItemNode().isValid()) return 100000; QRectF boundingRect = mapRectToScene(qmlItemNode().instanceBoundingRect()); float weight = point.x()- boundingRect.left() + point.y() - boundingRect.top() + boundingRect.right()- point.x() + boundingRect.bottom() - point.y() + (center() - point).manhattanLength() + sqrt(boundingRect.width() * boundingRect.height()) / 2 * iteration; return weight; } FormEditorItem::~FormEditorItem() { scene()->removeItemFromHash(this); } /* \brief returns the parent item skipping all proxyItem*/ FormEditorItem *FormEditorItem::parentItem() const { return qgraphicsitem_cast (QGraphicsItem::parentItem()); } FormEditorItem* FormEditorItem::fromQGraphicsItem(QGraphicsItem *graphicsItem) { return qgraphicsitem_cast(graphicsItem); } void FormEditorItem::paintBoundingRect(QPainter *painter) const { if (!m_boundingRect.isValid() || (QGraphicsItem::parentItem() == scene()->formLayerItem() && qFuzzyIsNull(m_borderWidth))) return; if (m_boundingRect.width() < 8 || m_boundingRect.height() < 8) return; QPen pen; pen.setCosmetic(true); pen.setJoinStyle(Qt::MiterJoin); const QColor frameColor(0xaa, 0xaa, 0xaa); static const QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); if (scene()->showBoundingRects()) { pen.setColor(frameColor.darker(150)); pen.setStyle(Qt::DotLine); painter->setPen(pen); painter->drawRect(m_boundingRect.adjusted(0., 0., -1., -1.)); } if (m_highlightBoundingRect) { pen.setColor(selectionColor); pen.setStyle(Qt::SolidLine); painter->setPen(pen); painter->drawRect(m_selectionBoundingRect.adjusted(0., 0., -1., -1.)); } } static void paintTextInPlaceHolderForInvisbleItem(QPainter *painter, const QString &id, const QString &typeName, const QRectF &boundingRect) { QString displayText = id.isEmpty() ? typeName : id; QTextOption textOption; textOption.setAlignment(Qt::AlignTop); textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); if (boundingRect.height() > 60) { QFont font; font.setStyleHint(QFont::SansSerif); font.setBold(true); font.setPixelSize(12); painter->setFont(font); QFontMetrics fm(font); painter->rotate(90); if (fm.horizontalAdvance(displayText) > (boundingRect.height() - 32) && displayText.length() > 4) { displayText = fm.elidedText(displayText, Qt::ElideRight, boundingRect.height() - 32, Qt::TextShowMnemonic); } QRectF rotatedBoundingBox; rotatedBoundingBox.setWidth(boundingRect.height()); rotatedBoundingBox.setHeight(12); rotatedBoundingBox.setY(-boundingRect.width() + 12); rotatedBoundingBox.setX(20); painter->setFont(font); painter->setPen(QColor(48, 48, 96, 255)); painter->setClipping(false); painter->drawText(rotatedBoundingBox, displayText, textOption); } } void paintDecorationInPlaceHolderForInvisbleItem(QPainter *painter, const QRectF &boundingRect) { qreal stripesWidth = 12; QRegion innerRegion = QRegion(boundingRect.adjusted(stripesWidth, stripesWidth, -stripesWidth, -stripesWidth).toRect()); QRegion outerRegion = QRegion(boundingRect.toRect()) - innerRegion; painter->setClipRegion(outerRegion); painter->setClipping(true); painter->fillRect(boundingRect.adjusted(1, 1, -1, -1), Qt::BDiagPattern); } void FormEditorItem::paintPlaceHolderForInvisbleItem(QPainter *painter) const { painter->save(); paintDecorationInPlaceHolderForInvisbleItem(painter, m_boundingRect); paintTextInPlaceHolderForInvisbleItem(painter, qmlItemNode().id(), qmlItemNode().simplifiedTypeName(), m_boundingRect); painter->restore(); } void FormEditorItem::paintComponentContentVisualisation(QPainter *painter, const QRectF &clippinRectangle) const { painter->setBrush(QColor(0, 0, 0, 150)); painter->fillRect(clippinRectangle, Qt::BDiagPattern); } QList FormEditorItem::offspringFormEditorItemsRecursive(const FormEditorItem *formEditorItem) const { QList formEditorItemList; foreach (QGraphicsItem *item, formEditorItem->childItems()) { FormEditorItem *formEditorItem = fromQGraphicsItem(item); if (formEditorItem) { formEditorItemList.append(formEditorItem); } } return formEditorItemList; } void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { if (!painter->isActive()) return; if (!qmlItemNode().isValid()) return; painter->save(); bool showPlaceHolder = qmlItemNode().instanceIsRenderPixmapNull() || !isContentVisible(); const bool isInStackedContainer = qmlItemNode().isInStackedContainer(); /* If already the parent is invisible then show nothing */ const bool hideCompletely = !isContentVisible() && (parentItem() && !parentItem()->isContentVisible()); if (isInStackedContainer) showPlaceHolder = qmlItemNode().instanceIsRenderPixmapNull() && isContentVisible(); QRegion clipRegion = painter->clipRegion(); if (clipRegion.contains(m_selectionBoundingRect.toRect().topLeft()) && clipRegion.contains(m_selectionBoundingRect.toRect().bottomRight())) painter->setClipRegion(boundingRect().toRect()); painter->setClipping(true); if (!hideCompletely) { if (showPlaceHolder) { if (scene()->showBoundingRects() && m_boundingRect.width() > 15 && m_boundingRect.height() > 15) paintPlaceHolderForInvisbleItem(painter); } else if (!isInStackedContainer || isContentVisible() ) { painter->save(); const QTransform &painterTransform = painter->transform(); if (painterTransform.m11() < 1.0 // horizontally scaled down? || painterTransform.m22() < 1.0 // vertically scaled down? || painterTransform.isRotating()) painter->setRenderHint(QPainter::SmoothPixmapTransform, true); if (m_blurContent) painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceBlurredRenderPixmap()); else painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceRenderPixmap()); painter->restore(); } } if (!qmlItemNode().isRootModelNode()) paintBoundingRect(painter); painter->restore(); } AbstractFormEditorTool* FormEditorItem::tool() const { return static_cast(scene())->currentTool(); } SnapLineMap FormEditorItem::topSnappingLines() const { return m_snappingLineCreator.topLines(); } SnapLineMap FormEditorItem::bottomSnappingLines() const { return m_snappingLineCreator.bottomLines(); } SnapLineMap FormEditorItem::leftSnappingLines() const { return m_snappingLineCreator.leftLines(); } SnapLineMap FormEditorItem::rightSnappingLines() const { return m_snappingLineCreator.rightLines(); } SnapLineMap FormEditorItem::horizontalCenterSnappingLines() const { return m_snappingLineCreator.horizontalCenterLines(); } SnapLineMap FormEditorItem::verticalCenterSnappingLines() const { return m_snappingLineCreator.verticalCenterLines(); } SnapLineMap FormEditorItem::topSnappingOffsets() const { return m_snappingLineCreator.topOffsets(); } SnapLineMap FormEditorItem::bottomSnappingOffsets() const { return m_snappingLineCreator.bottomOffsets(); } SnapLineMap FormEditorItem::leftSnappingOffsets() const { return m_snappingLineCreator.leftOffsets(); } SnapLineMap FormEditorItem::rightSnappingOffsets() const { return m_snappingLineCreator.rightOffsets(); } void FormEditorItem::updateSnappingLines(const QList &exceptionList, FormEditorItem *transformationSpaceItem) { m_snappingLineCreator.update(exceptionList, transformationSpaceItem, this); } QList FormEditorItem::childFormEditorItems() const { QList formEditorItemList; foreach (QGraphicsItem *item, childItems()) { FormEditorItem *formEditorItem = fromQGraphicsItem(item); if (formEditorItem) formEditorItemList.append(formEditorItem); } return formEditorItemList; } QList FormEditorItem::offspringFormEditorItems() const { return offspringFormEditorItemsRecursive(this); } bool FormEditorItem::isContainer() const { NodeMetaInfo nodeMetaInfo = qmlItemNode().modelNode().metaInfo(); if (nodeMetaInfo.isValid()) return !nodeMetaInfo.defaultPropertyIsComponent() && !nodeMetaInfo.isLayoutable(); return true; } QmlItemNode FormEditorItem::qmlItemNode() const { return m_qmlItemNode; } }