formeditoritem.cpp 13.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
12 13 14
** 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.
15
**
16 17
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
18 19 20 21 22
** 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.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25 26 27 28 29

#include "formeditoritem.h"
#include "formeditorscene.h"

#include <modelnode.h>
30
#include <nodehints.h>
31
#include <nodemetainfo.h>
32

Thomas Hartmann's avatar
Thomas Hartmann committed
33 34
#include <utils/theme/theme.h>

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
#include <QDebug>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QTimeLine>

#include <cmath>

namespace QmlDesigner {


FormEditorScene *FormEditorItem::scene() const {
    return qobject_cast<FormEditorScene*>(QGraphicsItem::scene());
}

FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene)
    : QGraphicsObject(scene->formLayerItem()),
    m_snappingLineCreator(this),
    m_qmlItemNode(qmlItemNode),
    m_borderWidth(1.0),
54
    m_highlightBoundingRect(false),
55
    m_blurContent(false),
56 57
    m_isContentVisible(true),
    m_isFormEditorVisible(true)
58
{
59
    setCacheMode(QGraphicsItem::NoCache);
60 61 62 63 64
    setup();
}

void FormEditorItem::setup()
{
65
    setAcceptedMouseButtons(Qt::NoButton);
66
    if (qmlItemNode().hasInstanceParent()) {
67
        setParentItem(scene()->itemForQmlItemNode(qmlItemNode().instanceParent().toQmlItemNode()));
68 69
        setOpacity(qmlItemNode().instanceValue("opacity").toDouble());
    }
70

Marco Bubke's avatar
Marco Bubke committed
71 72
    setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode().instanceValue("clip").toBool());

Thomas Hartmann's avatar
Thomas Hartmann committed
73
    if (NodeHints::fromModelNode(qmlItemNode()).forceClip())
74 75
        setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);

76 77 78
    if (QGraphicsItem::parentItem() == scene()->formLayerItem())
        m_borderWidth = 0.0;

79 80
    setContentVisible(qmlItemNode().instanceValue("visible").toBool());

81
    setFlag(QGraphicsItem::ItemIsMovable, true);
82
    setFlag(QGraphicsItem::ItemNegativeZStacksBehindParent, true);
83 84 85 86 87 88
    updateGeometry();
    updateVisibilty();
}

QRectF FormEditorItem::boundingRect() const
{
89
    return m_boundingRect;
90 91
}

Marco Bubke's avatar
Marco Bubke committed
92 93 94
QPainterPath FormEditorItem::shape() const
{
    QPainterPath painterPath;
95
    painterPath.addRect(m_selectionBoundingRect);
Marco Bubke's avatar
Marco Bubke committed
96 97 98 99 100 101

    return painterPath;
}

bool FormEditorItem::contains(const QPointF &point) const
{
102
    return m_selectionBoundingRect.contains(point);
Marco Bubke's avatar
Marco Bubke committed
103 104
}

105 106 107
void FormEditorItem::updateGeometry()
{
    prepareGeometryChange();
108 109 110
    m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
    m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect();
    m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect);
111
    setTransform(qmlItemNode().instanceTransformWithContentTransform());
112
    //the property for zValue is called z in QGraphicsObject
113 114
    if (qmlItemNode().instanceValue("z").isValid())
        setZValue(qmlItemNode().instanceValue("z").toDouble());
115 116 117 118 119 120 121 122 123 124 125
}

void FormEditorItem::updateVisibilty()
{
}

FormEditorView *FormEditorItem::formEditorView() const
{
    return scene()->editorView();
}

126 127 128 129 130 131 132 133
void FormEditorItem::setHighlightBoundingRect(bool highlight)
{
    if (m_highlightBoundingRect != highlight) {
        m_highlightBoundingRect = highlight;
        update();
    }
}

134 135
void FormEditorItem::blurContent(bool blurContent)
{
136 137 138
    if (!scene())
        return;

139 140 141 142 143 144
    if (m_blurContent != blurContent) {
        m_blurContent = blurContent;
        update();
    }
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
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;
}

162 163 164 165 166 167 168 169 170 171 172

bool FormEditorItem::isFormEditorVisible() const
{
    return m_isFormEditorVisible;
}
void FormEditorItem::setFormEditorVisible(bool isVisible)
{
    m_isFormEditorVisible = isVisible;
    setVisible(isVisible);
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
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;
}

195 196
FormEditorItem::~FormEditorItem()
{
Orgad Shaneh's avatar
Orgad Shaneh committed
197
    scene()->removeItemFromHash(this);
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
}

/* \brief returns the parent item skipping all proxyItem*/
FormEditorItem *FormEditorItem::parentItem() const
{
    return qgraphicsitem_cast<FormEditorItem*> (QGraphicsItem::parentItem());
}

FormEditorItem* FormEditorItem::fromQGraphicsItem(QGraphicsItem *graphicsItem)
{
    return qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
}

void FormEditorItem::paintBoundingRect(QPainter *painter) const
{
213
    if (!m_boundingRect.isValid()
214
        || (QGraphicsItem::parentItem() == scene()->formLayerItem() && qFuzzyIsNull(m_borderWidth)))
215 216
          return;

217
     if (m_boundingRect.width() < 8 || m_boundingRect.height() < 8)
218 219
         return;

220
    QPen pen;
221
    pen.setCosmetic(true);
222
    pen.setJoinStyle(Qt::MiterJoin);
223

224
    QColor frameColor("#AAAAAA");
Thomas Hartmann's avatar
Thomas Hartmann committed
225
    static const QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
226 227

    if (scene()->showBoundingRects()) {
228 229 230 231
        pen.setColor(frameColor.darker(150));
        pen.setStyle(Qt::DotLine);
        painter->setPen(pen);
        painter->drawRect(m_boundingRect.adjusted(0., 0., -1., -1.));
232

233
    }
234

235
    if (m_highlightBoundingRect) {
Thomas Hartmann's avatar
Thomas Hartmann committed
236
        pen.setColor(selectionColor);
237 238 239 240
        pen.setStyle(Qt::SolidLine);
        painter->setPen(pen);
        painter->drawRect(m_selectionBoundingRect.adjusted(0., 0., -1., -1.));
    }
241 242
}

243 244 245 246
static void paintTextInPlaceHolderForInvisbleItem(QPainter *painter,
                                                  const QString &id,
                                                  const QString &typeName,
                                                  const QRectF &boundingRect)
247
{
248
    QString displayText = id.isEmpty() ? typeName : id;
249

250
    QTextOption textOption;
251
    textOption.setAlignment(Qt::AlignTop);
252
    textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
253

254
    if (boundingRect.height() > 60) {
255 256 257
        QFont font;
        font.setStyleHint(QFont::SansSerif);
        font.setBold(true);
258
        font.setPixelSize(12);
259
        painter->setFont(font);
260

261
        QFontMetrics fm(font);
262
        painter->rotate(90);
263
        if (fm.width(displayText) > (boundingRect.height() - 32) && displayText.length() > 4) {
264

265
            displayText = fm.elidedText(displayText, Qt::ElideRight, boundingRect.height() - 32, Qt::TextShowMnemonic);
266
        }
267

268
        QRectF rotatedBoundingBox;
269
        rotatedBoundingBox.setWidth(boundingRect.height());
270
        rotatedBoundingBox.setHeight(12);
271
        rotatedBoundingBox.setY(-boundingRect.width() + 12);
272
        rotatedBoundingBox.setX(20);
273

274
        painter->setFont(font);
275
        painter->setPen(QColor(48, 48, 96, 255));
276
        painter->setClipping(false);
277
        painter->drawText(rotatedBoundingBox, displayText, textOption);
278
    }
279
}
Marco Bubke's avatar
Marco Bubke committed
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
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);
Marco Bubke's avatar
Marco Bubke committed
298
    painter->restore();
299
}
300

301 302 303 304 305
void FormEditorItem::paintComponentContentVisualisation(QPainter *painter, const QRectF &clippinRectangle) const
{
    painter->setBrush(QColor(0, 0, 0, 150));
    painter->fillRect(clippinRectangle, Qt::BDiagPattern);
}
306

307 308 309 310 311 312 313 314 315 316 317 318 319 320
QList<FormEditorItem *> FormEditorItem::offspringFormEditorItemsRecursive(const FormEditorItem *formEditorItem) const
{
    QList<FormEditorItem*> formEditorItemList;

    foreach (QGraphicsItem *item, formEditorItem->childItems()) {
        FormEditorItem *formEditorItem = fromQGraphicsItem(item);
        if (formEditorItem) {
            formEditorItemList.append(formEditorItem);
        }
    }

    return formEditorItemList;
}

321 322
void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
Marco Bubke's avatar
Marco Bubke committed
323 324 325
    if (!painter->isActive())
        return;

326 327 328 329 330
    if (!qmlItemNode().isValid())
        return;

    painter->save();

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    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();

    if (!hideCompletely) {
        if (showPlaceHolder) {
            if (scene()->showBoundingRects() && m_boundingRect.width() > 15 && m_boundingRect.height() > 15)
                paintPlaceHolderForInvisbleItem(painter);
        } else if (!isInStackedContainer || isContentVisible() ) {
            if (m_blurContent)
                painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceBlurredRenderPixmap());
            else
                painter->drawPixmap(m_paintedBoundingRect.topLeft(), qmlItemNode().instanceRenderPixmap());
        }
351
    }
352

Marco Bubke's avatar
Marco Bubke committed
353 354
    if (!qmlItemNode().isRootModelNode())
        paintBoundingRect(painter);
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

    painter->restore();
}

AbstractFormEditorTool* FormEditorItem::tool() const
{
    return static_cast<FormEditorScene*>(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<FormEditorItem*> &exceptionList,
                                         FormEditorItem *transformationSpaceItem)
{
418
    m_snappingLineCreator.update(exceptionList, transformationSpaceItem, this);
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
}


QList<FormEditorItem*> FormEditorItem::childFormEditorItems() const
{
    QList<FormEditorItem*> formEditorItemList;

    foreach (QGraphicsItem *item, childItems()) {
        FormEditorItem *formEditorItem = fromQGraphicsItem(item);
        if (formEditorItem)
            formEditorItemList.append(formEditorItem);
    }

    return formEditorItemList;
}

435 436 437 438 439
QList<FormEditorItem *> FormEditorItem::offspringFormEditorItems() const
{
    return offspringFormEditorItemsRecursive(this);
}

440 441
bool FormEditorItem::isContainer() const
{
442 443 444
    NodeMetaInfo nodeMetaInfo = qmlItemNode().modelNode().metaInfo();

    if (nodeMetaInfo.isValid())
445
        return !nodeMetaInfo.defaultPropertyIsComponent() && !nodeMetaInfo.isLayoutable();
446

447
    return true;
448 449 450 451 452 453 454 455 456
}

QmlItemNode FormEditorItem::qmlItemNode() const
{
    return m_qmlItemNode;
}

}