forked from qt-creator/qt-creator
QmlDesigner: Adding formerly commercial features
This patch adds the formerly commercial only features to the open source version of Qt Quick Designer. The license is GPL v3. Change-Id: I8f04e626766088298f2e827e7ef136e3c850fd78 Reviewed-by: Alessandro Portale <alessandro.portale@theqtcompany.com>
This commit is contained in:
committed by
Alessandro Portale
parent
897718be05
commit
3236d146a1
@@ -0,0 +1,15 @@
|
||||
{
|
||||
\"Name\" : \"QmlDesignerExtension\",
|
||||
\"Version\" : \"$$QTCREATOR_VERSION\",
|
||||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
|
||||
\"Vendor\" : \"The Qt Company Ltd\",
|
||||
\"Copyright\" : \"(C) 2015 The Qt Company Ltd\",
|
||||
\"License\" : [ \"Commercial Usage\",
|
||||
\"\",
|
||||
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\"
|
||||
],
|
||||
\"Category\" : \"Qt Quick\",
|
||||
\"Description\" : \"Extensions for the Qt Quick Designer.\",
|
||||
\"Url\" : \"http://www.qt.io\",
|
||||
$$dependencyList
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "colortool.h"
|
||||
|
||||
#include "formeditorscene.h"
|
||||
#include "formeditorview.h"
|
||||
#include "formeditorwidget.h"
|
||||
#include "itemutilfunctions.h"
|
||||
#include "formeditoritem.h"
|
||||
|
||||
#include "resizehandleitem.h"
|
||||
|
||||
#include "nodemetainfo.h"
|
||||
#include "qmlitemnode.h"
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <abstractaction.h>
|
||||
#include <designeractionmanager.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
#include <QUrl>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ColorToolAction : public AbstractAction
|
||||
{
|
||||
public:
|
||||
ColorToolAction() : AbstractAction(QCoreApplication::translate("ColorToolAction","Edit Color")) {}
|
||||
|
||||
QByteArray category() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray menuId() const
|
||||
{
|
||||
return "ColorTool";
|
||||
}
|
||||
|
||||
int priority() const
|
||||
{
|
||||
return CustomActionsPriority;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return Action;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isVisible(const SelectionContext &selectionContext) const
|
||||
{
|
||||
if (selectionContext.singleNodeIsSelected())
|
||||
return selectionContext.currentSingleSelectedNode().metaInfo().hasProperty("color");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEnabled(const SelectionContext &selectionContext) const
|
||||
{
|
||||
return isVisible(selectionContext);
|
||||
}
|
||||
};
|
||||
|
||||
ColorTool::ColorTool()
|
||||
: QObject(), AbstractCustomTool()
|
||||
{
|
||||
ColorToolAction *colorToolAction = new ColorToolAction;
|
||||
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(colorToolAction);
|
||||
connect(colorToolAction->action(),
|
||||
SIGNAL(triggered()),
|
||||
this,
|
||||
SLOT(changeToColorTool()));
|
||||
}
|
||||
|
||||
ColorTool::~ColorTool()
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::clear()
|
||||
{
|
||||
m_colorDialog.data()->deleteLater();
|
||||
|
||||
AbstractFormEditorTool::clear();
|
||||
}
|
||||
|
||||
void ColorTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mousePressEvent(itemList, event);
|
||||
}
|
||||
|
||||
void ColorTool::mouseMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::hoverMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::keyPressEvent(QKeyEvent * /*keyEvent*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mouseReleaseEvent(itemList, event);
|
||||
}
|
||||
|
||||
|
||||
void ColorTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event);
|
||||
}
|
||||
|
||||
void ColorTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList)
|
||||
{
|
||||
if (m_colorDialog.isNull())
|
||||
return;
|
||||
|
||||
if (removedItemList.contains(m_formEditorItem.data()))
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
void ColorTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
|
||||
{
|
||||
if (m_colorDialog.data()
|
||||
&& m_oldColor.isValid())
|
||||
m_formEditorItem.data()->qmlItemNode().setVariantProperty("color", m_oldColor);
|
||||
|
||||
if (!itemList.isEmpty()
|
||||
&& itemList.first()->qmlItemNode().modelNode().metaInfo().hasProperty("color")) {
|
||||
m_formEditorItem = itemList.first();
|
||||
m_oldColor = m_formEditorItem.data()->qmlItemNode().modelValue("color").value<QColor>();
|
||||
|
||||
if (m_colorDialog.isNull()) {
|
||||
m_colorDialog = new QColorDialog(view()->formEditorWidget()->parentWidget());
|
||||
m_colorDialog.data()->setCurrentColor(m_oldColor);
|
||||
|
||||
connect(m_colorDialog.data(), SIGNAL(accepted()), SLOT(colorDialogAccepted()));
|
||||
connect(m_colorDialog.data(), SIGNAL(rejected()), SLOT(colorDialogRejected()));
|
||||
connect(m_colorDialog.data(), SIGNAL(currentColorChanged(QColor)), SLOT(currentColorChanged(QColor)));
|
||||
|
||||
m_colorDialog.data()->exec();
|
||||
}
|
||||
} else {
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
int ColorTool::wantHandleItem(const ModelNode &modelNode) const
|
||||
{
|
||||
if (modelNode.metaInfo().hasProperty("color"))
|
||||
return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString ColorTool::name() const
|
||||
{
|
||||
return tr("Color Tool");
|
||||
}
|
||||
|
||||
void ColorTool::colorDialogAccepted()
|
||||
{
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
void ColorTool::colorDialogRejected()
|
||||
{
|
||||
if (m_formEditorItem) {
|
||||
if (m_oldColor.isValid())
|
||||
m_formEditorItem.data()->qmlItemNode().setVariantProperty("color", m_oldColor);
|
||||
else
|
||||
m_formEditorItem.data()->qmlItemNode().removeProperty("color");
|
||||
|
||||
}
|
||||
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
void ColorTool::currentColorChanged(const QColor &color)
|
||||
{
|
||||
if (m_formEditorItem) {
|
||||
m_formEditorItem.data()->qmlItemNode().setVariantProperty("color", color);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorTool::changeToColorTool()
|
||||
{
|
||||
view()->changeToCustomTool(this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef COLORTOOL_H
|
||||
#define COLORTOOL_H
|
||||
|
||||
#include "abstractcustomtool.h"
|
||||
#include "selectionindicator.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QColorDialog>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ColorTool : public QObject, public AbstractCustomTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorTool();
|
||||
~ColorTool();
|
||||
|
||||
void mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void keyReleaseEvent(QKeyEvent *keyEvent) override;
|
||||
|
||||
void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
|
||||
void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
|
||||
void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
|
||||
void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
int wantHandleItem(const ModelNode &modelNode) const override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
private slots:
|
||||
void colorDialogAccepted();
|
||||
void colorDialogRejected();
|
||||
void currentColorChanged(const QColor &color);
|
||||
void changeToColorTool();
|
||||
|
||||
private:
|
||||
QPointer<QColorDialog> m_colorDialog;
|
||||
QPointer<FormEditorItem> m_formEditorItem;
|
||||
QColor m_oldColor;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // COLORTOOL_H
|
||||
|
@@ -0,0 +1,3 @@
|
||||
HEADERS += $$PWD/colortool.h
|
||||
|
||||
SOURCES += $$PWD/colortool.cpp
|
@@ -0,0 +1,592 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "bindingmodel.h"
|
||||
|
||||
#include "connectionview.h"
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
#include <nodeproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <rewritingexception.h>
|
||||
#include <rewritertransaction.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QItemEditorFactory>
|
||||
#include <QComboBox>
|
||||
#include <QMessageBox>
|
||||
#include <QStyleFactory>
|
||||
#include <QTimer>
|
||||
|
||||
namespace {
|
||||
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
TargetPropertyNameRow = 1,
|
||||
SourceModelNodeRow = 2,
|
||||
SourcePropertyNameRow = 3
|
||||
};
|
||||
|
||||
bool compareBindingProperties(const QmlDesigner::BindingProperty &bindingProperty01, const QmlDesigner::BindingProperty &bindingProperty02)
|
||||
{
|
||||
if (bindingProperty01.parentModelNode() != bindingProperty02.parentModelNode())
|
||||
return false;
|
||||
if (bindingProperty01.name() != bindingProperty02.name())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} //internal namespace
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
BindingModel::BindingModel(ConnectionView *parent) :
|
||||
QStandardItemModel(parent),
|
||||
m_connectionView(parent),
|
||||
m_lock(false),
|
||||
m_handleDataChanged(false)
|
||||
{
|
||||
connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(handleDataChanged(QModelIndex,QModelIndex)));
|
||||
}
|
||||
|
||||
void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBinding(bindingProperty);
|
||||
|
||||
if (rowNumber == -1) {
|
||||
addBindingProperty(bindingProperty);
|
||||
} else {
|
||||
updateBindingProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void BindingModel::bindingRemoved(const BindingProperty &bindingProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBinding(bindingProperty);
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void BindingModel::selectionChanged(const QList<ModelNode> &selectedNodes)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
m_selectedModelNodes = selectedNodes;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
ConnectionView *BindingModel::connectionView() const
|
||||
{
|
||||
return m_connectionView;
|
||||
}
|
||||
|
||||
BindingProperty BindingModel::bindingPropertyForRow(int rowNumber) const
|
||||
{
|
||||
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.bindingProperty(targetPropertyName.toLatin1());
|
||||
|
||||
return BindingProperty();
|
||||
}
|
||||
|
||||
QStringList BindingModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
|
||||
{
|
||||
const ModelNode modelNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (!modelNode.isValid()) {
|
||||
qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
NodeMetaInfo metaInfo = modelNode.metaInfo();
|
||||
|
||||
if (metaInfo.isValid()) {
|
||||
QStringList possibleProperties;
|
||||
foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
|
||||
if (metaInfo.propertyIsWritable(propertyName))
|
||||
possibleProperties << QString::fromLatin1(propertyName);
|
||||
}
|
||||
|
||||
return possibleProperties;
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QStringList BindingModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
|
||||
{
|
||||
const QString expression = bindingProperty.expression();
|
||||
const QStringList stringlist = expression.split(QLatin1String("."));
|
||||
|
||||
TypeName typeName;
|
||||
|
||||
if (bindingProperty.parentModelNode().metaInfo().isValid()) {
|
||||
typeName = bindingProperty.parentModelNode().metaInfo().propertyTypeName(bindingProperty.name());
|
||||
} else {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
|
||||
}
|
||||
|
||||
const QString id = stringlist.first();
|
||||
|
||||
ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
|
||||
|
||||
if (!modelNode.isValid()) {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
NodeMetaInfo metaInfo = modelNode.metaInfo();
|
||||
|
||||
QStringList possibleProperties;
|
||||
|
||||
foreach (VariantProperty variantProperty, modelNode.variantProperties()) {
|
||||
if (variantProperty.isDynamic())
|
||||
possibleProperties << QString::fromLatin1((variantProperty.name()));
|
||||
}
|
||||
|
||||
foreach (BindingProperty bindingProperty, modelNode.bindingProperties()) {
|
||||
if (bindingProperty.isDynamic())
|
||||
possibleProperties << QString::fromLatin1((bindingProperty.name()));
|
||||
}
|
||||
|
||||
if (metaInfo.isValid()) {
|
||||
foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
|
||||
if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check
|
||||
possibleProperties << QString::fromLatin1(propertyName);
|
||||
}
|
||||
} else {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
|
||||
}
|
||||
|
||||
return possibleProperties;
|
||||
}
|
||||
|
||||
void BindingModel::deleteBindindByRow(int rowNumber)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
|
||||
}
|
||||
|
||||
resetModel();
|
||||
}
|
||||
|
||||
static PropertyName unusedProperty(const ModelNode &modelNode)
|
||||
{
|
||||
PropertyName propertyName = "none";
|
||||
if (modelNode.metaInfo().isValid()) {
|
||||
foreach (const PropertyName &propertyName, modelNode.metaInfo().propertyNames()) {
|
||||
if (modelNode.metaInfo().propertyIsWritable(propertyName) && !modelNode.hasProperty(propertyName))
|
||||
return propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
void BindingModel::addBindingForCurrentNode()
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
ModelNode modelNode = connectionView()->selectedModelNodes().first();
|
||||
if (modelNode.isValid()) {
|
||||
try {
|
||||
modelNode.bindingProperty(unusedProperty(modelNode)).setExpression(QLatin1String("none.none"));
|
||||
} catch (RewritingException &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
clear();
|
||||
|
||||
QStringList labels;
|
||||
|
||||
labels << tr("Item");
|
||||
labels <<tr("Property");
|
||||
labels <<tr("Source Item");
|
||||
labels <<tr("Source Property");
|
||||
|
||||
setHorizontalHeaderLabels(labels);
|
||||
|
||||
foreach (const ModelNode modelNode, m_selectedModelNodes) {
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void BindingModel::addBindingProperty(const BindingProperty &property)
|
||||
{
|
||||
QStandardItem *idItem;
|
||||
QStandardItem *targetPropertyNameItem;
|
||||
QStandardItem *sourceIdItem;
|
||||
QStandardItem *sourcePropertyNameItem;
|
||||
|
||||
QString idLabel = property.parentModelNode().id();
|
||||
if (idLabel.isEmpty())
|
||||
idLabel = QString::fromLatin1(property.parentModelNode().simplifiedTypeName());
|
||||
idItem = new QStandardItem(idLabel);
|
||||
updateCustomData(idItem, property);
|
||||
targetPropertyNameItem = new QStandardItem(QString::fromLatin1(property.name()));
|
||||
QList<QStandardItem*> items;
|
||||
|
||||
items.append(idItem);
|
||||
items.append(targetPropertyNameItem);
|
||||
|
||||
QString sourceNodeName;
|
||||
QString sourcePropertyName;
|
||||
getExpressionStrings(property, &sourceNodeName, &sourcePropertyName);
|
||||
|
||||
sourceIdItem = new QStandardItem(sourceNodeName);
|
||||
sourcePropertyNameItem = new QStandardItem(sourcePropertyName);
|
||||
|
||||
items.append(sourceIdItem);
|
||||
items.append(sourcePropertyNameItem);
|
||||
appendRow(items);
|
||||
}
|
||||
|
||||
void BindingModel::updateBindingProperty(int rowNumber)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
QString targetPropertyName = QString::fromLatin1(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
|
||||
QString sourceNodeName;
|
||||
QString sourcePropertyName;
|
||||
getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
|
||||
updateDisplayRole(rowNumber, SourceModelNodeRow, sourceNodeName);
|
||||
updateDisplayRole(rowNumber, SourcePropertyNameRow, sourcePropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::addModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
foreach (const BindingProperty &bindingProperty, modelNode.bindingProperties()) {
|
||||
addBindingProperty(bindingProperty);
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::updateExpression(int row)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(row);
|
||||
|
||||
const QString sourceNode = data(index(row, SourceModelNodeRow)).toString();
|
||||
const QString sourceProperty = data(index(row, SourcePropertyNameRow)).toString();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updateExpression"));
|
||||
try {
|
||||
bindingProperty.setExpression(expression);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::updatePropertyName(int rowNumber)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toLatin1();
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (!newName.isEmpty()) {
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updatePropertyName"));
|
||||
try {
|
||||
if (bindingProperty.isDynamic()) {
|
||||
targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
|
||||
} else {
|
||||
targetNode.bindingProperty(newName).setExpression(expression);
|
||||
}
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
|
||||
QStandardItem* idItem = item(rowNumber, 0);
|
||||
BindingProperty newBindingProperty = targetNode.bindingProperty(newName);
|
||||
updateCustomData(idItem, newBindingProperty);
|
||||
|
||||
} else {
|
||||
qWarning() << "BindingModel::updatePropertyName invalid property name";
|
||||
}
|
||||
}
|
||||
|
||||
ModelNode BindingModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
|
||||
{
|
||||
ModelNode modelNode;
|
||||
|
||||
if (id != QLatin1String("parent")) {
|
||||
modelNode = connectionView()->modelNodeForId(id);
|
||||
} else {
|
||||
if (targetNode.hasParentProperty()) {
|
||||
modelNode = targetNode.parentProperty().parentModelNode();
|
||||
}
|
||||
}
|
||||
return modelNode;
|
||||
}
|
||||
|
||||
void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty)
|
||||
{
|
||||
item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(bindingProperty.name(), Qt::UserRole + 2);
|
||||
}
|
||||
|
||||
int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
|
||||
{
|
||||
for (int i=0; i < rowCount(); i++) {
|
||||
if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
|
||||
return i;
|
||||
}
|
||||
//not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
|
||||
{
|
||||
//### todo we assume no expressions yet
|
||||
|
||||
const QString expression = bindingProperty.expression();
|
||||
|
||||
if (true) {
|
||||
const QStringList stringList = expression.split(QLatin1String("."));
|
||||
|
||||
*sourceNode = stringList.first();
|
||||
|
||||
QString propertyName;
|
||||
|
||||
for (int i=1; i < stringList.count(); i++) {
|
||||
propertyName += stringList.at(i);
|
||||
if (i != stringList.count() - 1)
|
||||
propertyName += QLatin1String(".");
|
||||
}
|
||||
*sourceProperty = propertyName;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BindingModel::updateDisplayRole(int row, int columns, const QString &string)
|
||||
{
|
||||
QModelIndex modelIndex = index(row, columns);
|
||||
if (data(modelIndex).toString() != string)
|
||||
setData(modelIndex, string);
|
||||
}
|
||||
|
||||
void BindingModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
if (!m_handleDataChanged)
|
||||
return;
|
||||
|
||||
if (topLeft != bottomRight) {
|
||||
qWarning() << "BindingModel::handleDataChanged multi edit?";
|
||||
return;
|
||||
}
|
||||
|
||||
m_lock = true;
|
||||
|
||||
int currentColumn = topLeft.column();
|
||||
int currentRow = topLeft.row();
|
||||
|
||||
switch (currentColumn) {
|
||||
case TargetModelNodeRow: {
|
||||
//updating user data
|
||||
} break;
|
||||
case TargetPropertyNameRow: {
|
||||
updatePropertyName(currentRow);
|
||||
} break;
|
||||
case SourceModelNodeRow: {
|
||||
updateExpression(currentRow);
|
||||
} break;
|
||||
case SourcePropertyNameRow: {
|
||||
updateExpression(currentRow);
|
||||
} break;
|
||||
|
||||
default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
|
||||
}
|
||||
|
||||
m_lock = false;
|
||||
}
|
||||
|
||||
void BindingModel::handleException()
|
||||
{
|
||||
QMessageBox::warning(0, tr("Error"), m_exceptionError);
|
||||
resetModel();
|
||||
}
|
||||
|
||||
BindingDelegate::BindingDelegate(QWidget *parent) : QStyledItemDelegate(parent)
|
||||
{
|
||||
static QItemEditorFactory *factory = 0;
|
||||
if (factory == 0) {
|
||||
factory = new QItemEditorFactory;
|
||||
QItemEditorCreatorBase *creator
|
||||
= new QItemEditorCreator<BindingComboBox>("text");
|
||||
factory->registerEditor(QVariant::String, creator);
|
||||
}
|
||||
|
||||
setItemEditorFactory(factory);
|
||||
}
|
||||
|
||||
QWidget *BindingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
|
||||
|
||||
const BindingModel *model = qobject_cast<const BindingModel*>(index.model());
|
||||
|
||||
model->connectionView()->allModelNodes();
|
||||
|
||||
BindingComboBox *bindingComboBox = qobject_cast<BindingComboBox*>(widget);
|
||||
|
||||
if (!model) {
|
||||
qWarning() << "BindingDelegate::createEditor no model";
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!model->connectionView()) {
|
||||
qWarning() << "BindingDelegate::createEditor no connection view";
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!bindingComboBox) {
|
||||
qWarning() << "BindingDelegate::createEditor no bindingComboBox";
|
||||
return widget;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(index.row());
|
||||
|
||||
switch (index.column()) {
|
||||
case TargetModelNodeRow: {
|
||||
return 0; //no editor
|
||||
foreach (const ModelNode &modelNode, model->connectionView()->allModelNodes()) {
|
||||
if (!modelNode.id().isEmpty()) {
|
||||
bindingComboBox->addItem(modelNode.id());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TargetPropertyNameRow: {
|
||||
bindingComboBox->addItems(model->possibleTargetProperties(bindingProperty));
|
||||
} break;
|
||||
case SourceModelNodeRow: {
|
||||
foreach (const ModelNode &modelNode, model->connectionView()->allModelNodes()) {
|
||||
if (!modelNode.id().isEmpty()) {
|
||||
bindingComboBox->addItem(modelNode.id());
|
||||
}
|
||||
}
|
||||
if (!bindingProperty.parentModelNode().isRootNode())
|
||||
bindingComboBox->addItem(QLatin1String("parent"));
|
||||
} break;
|
||||
case SourcePropertyNameRow: {
|
||||
bindingComboBox->addItems(model->possibleSourceProperties(bindingProperty));
|
||||
} break;
|
||||
default: qWarning() << "BindingDelegate::createEditor column" << index.column();
|
||||
}
|
||||
|
||||
connect(bindingComboBox, SIGNAL(activated(QString)), this, SLOT(emitCommitData(QString)));
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
void BindingDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.state &= ~QStyle::State_HasFocus;
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
void BindingDelegate::emitCommitData(const QString & /*text*/)
|
||||
{
|
||||
BindingComboBox *bindingComboBox = qobject_cast<BindingComboBox*>(sender());
|
||||
emit commitData(bindingComboBox);
|
||||
}
|
||||
|
||||
BindingComboBox::BindingComboBox(QWidget *parent) : QComboBox(parent)
|
||||
{
|
||||
static QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("windows")));
|
||||
setEditable(true);
|
||||
if (style)
|
||||
setStyle(style.data());
|
||||
}
|
||||
|
||||
QString BindingComboBox::text() const
|
||||
{
|
||||
return currentText();
|
||||
}
|
||||
|
||||
void BindingComboBox::setText(const QString &text)
|
||||
{
|
||||
setEditText(text);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,122 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef BINDINGMODEL_H
|
||||
#define BINDINGMODEL_H
|
||||
|
||||
#include <modelnode.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <bindingproperty.h>
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QStandardItemModel>
|
||||
#include <QComboBox>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class Model;
|
||||
namespace Internal {
|
||||
|
||||
class ConnectionView;
|
||||
|
||||
class BindingModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BindingModel(ConnectionView *parent = 0);
|
||||
void bindingChanged(const BindingProperty &bindingProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
void selectionChanged(const QList<ModelNode> &selectedNodes);
|
||||
|
||||
ConnectionView *connectionView() const;
|
||||
BindingProperty bindingPropertyForRow(int rowNumber) const;
|
||||
QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
|
||||
QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
|
||||
void deleteBindindByRow(int rowNumber);
|
||||
void addBindingForCurrentNode();
|
||||
|
||||
protected:
|
||||
void resetModel();
|
||||
void addBindingProperty(const BindingProperty &property);
|
||||
void updateBindingProperty(int rowNumber);
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
void updateExpression(int row);
|
||||
void updatePropertyName(int rowNumber);
|
||||
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
|
||||
void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
|
||||
int findRowForBinding(const BindingProperty &bindingProperty);
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
private slots:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
||||
private:
|
||||
QList<ModelNode> m_selectedModelNodes;
|
||||
ConnectionView *m_connectionView;
|
||||
bool m_lock;
|
||||
bool m_handleDataChanged;
|
||||
QString m_exceptionError;
|
||||
|
||||
};
|
||||
|
||||
class BindingDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BindingDelegate(QWidget *parent = 0);
|
||||
|
||||
virtual QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void emitCommitData(const QString &text);
|
||||
};
|
||||
|
||||
class BindingComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText USER true)
|
||||
public:
|
||||
BindingComboBox(QWidget *parent = 0);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // BINDINGMODEL_H
|
@@ -0,0 +1,19 @@
|
||||
VPATH += $$PWD
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += connectionview.h \
|
||||
connectionviewwidget.h \
|
||||
connectionmodel.h \
|
||||
bindingmodel.h \
|
||||
dynamicpropertiesmodel.h
|
||||
|
||||
SOURCES += connectionview.cpp \
|
||||
connectionviewwidget.cpp \
|
||||
connectionmodel.cpp \
|
||||
bindingmodel.cpp \
|
||||
dynamicpropertiesmodel.cpp
|
||||
|
||||
FORMS += \
|
||||
connectionviewwidget.ui
|
||||
|
||||
RESOURCES += connectioneditor.qrc
|
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/connectionview">
|
||||
<file>stylesheet.css</file>
|
||||
<file>panel.png</file>
|
||||
<file>panelselected.png</file>
|
||||
<file>plus.png</file>
|
||||
<file>minus.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@@ -0,0 +1,514 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "connectionmodel.h"
|
||||
#include "connectionview.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <signalhandlerproperty.h>
|
||||
#include <rewritertransaction.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
#include <exception.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTableView>
|
||||
#include <QTimer>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QStyleFactory>
|
||||
|
||||
namespace {
|
||||
|
||||
QStringList prependOnForSignalHandler(const QStringList &signalNames)
|
||||
{
|
||||
QStringList signalHandlerNames;
|
||||
foreach (const QString &signalName, signalNames) {
|
||||
QString signalHandlerName = signalName;
|
||||
if (!signalHandlerName.isEmpty()) {
|
||||
QChar firstChar = signalHandlerName.at(0).toUpper();
|
||||
signalHandlerName[0] = firstChar;
|
||||
signalHandlerName.prepend(QLatin1String("on"));
|
||||
signalHandlerNames.append(signalHandlerName);
|
||||
}
|
||||
}
|
||||
return signalHandlerNames;
|
||||
}
|
||||
|
||||
QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList)
|
||||
{
|
||||
QStringList stringList;
|
||||
foreach (QmlDesigner::PropertyName propertyName, propertyNameList) {
|
||||
stringList << QString::fromLatin1(propertyName);
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
|
||||
bool isConnection(const QmlDesigner::ModelNode &modelNode)
|
||||
{
|
||||
return (modelNode.type() == "Connections"
|
||||
|| modelNode.type() == "QtQuick.Connections"
|
||||
|| modelNode.type() == "Qt.Connections");
|
||||
|
||||
}
|
||||
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
TargetPropertyNameRow = 1,
|
||||
SourceRow = 2
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
ConnectionModel::ConnectionModel(ConnectionView *parent) : QStandardItemModel(parent), m_connectionView(parent), m_lock(false)
|
||||
{
|
||||
connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(handleDataChanged(QModelIndex,QModelIndex)));
|
||||
}
|
||||
|
||||
void ConnectionModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
clear();
|
||||
|
||||
QStringList labels;
|
||||
|
||||
labels << tr("Target");
|
||||
labels << tr("Signal Handler");
|
||||
labels <<tr("Action");
|
||||
|
||||
setHorizontalHeaderLabels(labels);
|
||||
|
||||
if (connectionView()->isAttached()) {
|
||||
foreach (const ModelNode modelNode, connectionView()->allModelNodes()) {
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0);
|
||||
connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const
|
||||
{
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.signalHandlerProperty(targetPropertyName.toLatin1());
|
||||
|
||||
return SignalHandlerProperty();
|
||||
}
|
||||
|
||||
void ConnectionModel::addModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
if (isConnection(modelNode))
|
||||
addConnection(modelNode);
|
||||
}
|
||||
|
||||
void ConnectionModel::addConnection(const ModelNode &modelNode)
|
||||
{
|
||||
foreach (const AbstractProperty &property, modelNode.properties()) {
|
||||
if (property.isSignalHandlerProperty() && property.name() != "target") {
|
||||
addSignalHandler(property.toSignalHandlerProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionModel::addSignalHandler(const SignalHandlerProperty &signalHandlerProperty)
|
||||
{
|
||||
QStandardItem *targetItem;
|
||||
QStandardItem *signalItem;
|
||||
QStandardItem *actionItem;
|
||||
|
||||
QString idLabel;
|
||||
|
||||
ModelNode connectionsModelNode = signalHandlerProperty.parentModelNode();
|
||||
|
||||
if (connectionsModelNode.bindingProperty("target").isValid()) {
|
||||
idLabel =connectionsModelNode.bindingProperty("target").expression();
|
||||
}
|
||||
|
||||
targetItem = new QStandardItem(idLabel);
|
||||
updateCustomData(targetItem, signalHandlerProperty);
|
||||
const QString propertyName = QString::fromLatin1(signalHandlerProperty.name());
|
||||
const QString source = signalHandlerProperty.source();
|
||||
|
||||
signalItem = new QStandardItem(propertyName);
|
||||
QList<QStandardItem*> items;
|
||||
|
||||
items.append(targetItem);
|
||||
items.append(signalItem);
|
||||
|
||||
actionItem = new QStandardItem(source);
|
||||
|
||||
items.append(actionItem);
|
||||
|
||||
appendRow(items);
|
||||
}
|
||||
|
||||
void ConnectionModel::removeModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
if (isConnection(modelNode))
|
||||
removeConnection(modelNode);
|
||||
}
|
||||
|
||||
void ConnectionModel::removeConnection(const ModelNode & /*modelNode*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ConnectionModel::updateSource(int row)
|
||||
{
|
||||
SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(row);
|
||||
|
||||
const QString sourceString = data(index(row, SourceRow)).toString();
|
||||
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("ConnectionModel::updateSource"));
|
||||
|
||||
try {
|
||||
signalHandlerProperty.setSource(sourceString);
|
||||
transaction.commit();
|
||||
}
|
||||
catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConnectionModel::updateSignalName(int rowNumber)
|
||||
{
|
||||
SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(rowNumber);
|
||||
|
||||
const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toLatin1();
|
||||
const QString source = signalHandlerProperty.source();
|
||||
ModelNode connectionNode = signalHandlerProperty.parentModelNode();
|
||||
|
||||
if (!newName.isEmpty()) {
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("ConnectionModel::updateSignalName"));
|
||||
try {
|
||||
connectionNode.signalHandlerProperty(newName).setSource(source);
|
||||
connectionNode.removeProperty(signalHandlerProperty.name());
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
QStandardItem* idItem = item(rowNumber, 0);
|
||||
SignalHandlerProperty newSignalHandlerProperty = connectionNode.signalHandlerProperty(newName);
|
||||
updateCustomData(idItem, newSignalHandlerProperty);
|
||||
|
||||
} else {
|
||||
qWarning() << "BindingModel::updatePropertyName invalid property name";
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionModel::updateTargetNode(int rowNumber)
|
||||
{
|
||||
SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(rowNumber);
|
||||
const QString newTarget = data(index(rowNumber, TargetModelNodeRow)).toString();
|
||||
ModelNode connectionNode = signalHandlerProperty.parentModelNode();
|
||||
|
||||
if (!newTarget.isEmpty()) {
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("ConnectionModel::updateTargetNode"));
|
||||
try {
|
||||
connectionNode.bindingProperty("target").setExpression(newTarget);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
QStandardItem* idItem = item(rowNumber, 0);
|
||||
updateCustomData(idItem, signalHandlerProperty);
|
||||
|
||||
} else {
|
||||
qWarning() << "BindingModel::updatePropertyName invalid target id";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty)
|
||||
{
|
||||
item->setData(signalHandlerProperty.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(signalHandlerProperty.name(), Qt::UserRole + 2);
|
||||
}
|
||||
|
||||
ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const
|
||||
{
|
||||
BindingProperty bindingProperty = connection.bindingProperty("target");
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
if (bindingProperty.expression() == QLatin1String("parent"))
|
||||
return connection.parentProperty().parentModelNode();
|
||||
return connectionView()->modelNodeForId(bindingProperty.expression());
|
||||
}
|
||||
|
||||
return ModelNode();
|
||||
}
|
||||
|
||||
void ConnectionModel::addConnection()
|
||||
{
|
||||
ModelNode rootModelNode = connectionView()->rootModelNode();
|
||||
|
||||
if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) {
|
||||
|
||||
NodeMetaInfo nodeMetaInfo = connectionView()->model()->metaInfo("QtQuick.Connections");
|
||||
|
||||
if (nodeMetaInfo.isValid()) {
|
||||
RewriterTransaction transaction =
|
||||
connectionView()->beginRewriterTransaction(QByteArrayLiteral("ConnectionModel::addConnection"));
|
||||
try {
|
||||
ModelNode newNode = connectionView()->createModelNode("QtQuick.Connections",
|
||||
nodeMetaInfo.majorVersion(),
|
||||
nodeMetaInfo.minorVersion());
|
||||
|
||||
rootModelNode.nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()).reparentHere(newNode);
|
||||
newNode.signalHandlerProperty("onClicked").setSource(QLatin1String("print(\"clicked\")"));
|
||||
|
||||
if (connectionView()->selectedModelNodes().count() == 1
|
||||
&& !connectionView()->selectedModelNodes().first().id().isEmpty()) {
|
||||
ModelNode selectedNode = connectionView()->selectedModelNodes().first();
|
||||
newNode.bindingProperty("target").setExpression(selectedNode.id());
|
||||
} else {
|
||||
newNode.bindingProperty("target").setExpression(QLatin1String("parent"));
|
||||
}
|
||||
transaction.commit();
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionModel::bindingPropertyChanged(const BindingProperty &bindingProperty)
|
||||
{
|
||||
if (isConnection(bindingProperty.parentModelNode()))
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void ConnectionModel::variantPropertyChanged(const VariantProperty &variantProperty)
|
||||
{
|
||||
if (isConnection(variantProperty.parentModelNode()))
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void ConnectionModel::deleteConnectionByRow(int currentRow)
|
||||
{
|
||||
signalHandlerPropertyForRow(currentRow).parentModelNode().destroy();
|
||||
}
|
||||
|
||||
void ConnectionModel::handleException()
|
||||
{
|
||||
QMessageBox::warning(0, tr("Error"), m_exceptionError);
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
if (topLeft != bottomRight) {
|
||||
qWarning() << "ConnectionModel::handleDataChanged multi edit?";
|
||||
return;
|
||||
}
|
||||
|
||||
m_lock = true;
|
||||
|
||||
int currentColumn = topLeft.column();
|
||||
int currentRow = topLeft.row();
|
||||
|
||||
switch (currentColumn) {
|
||||
case TargetModelNodeRow: {
|
||||
updateTargetNode(currentRow);
|
||||
} break;
|
||||
case TargetPropertyNameRow: {
|
||||
updateSignalName(currentRow);
|
||||
} break;
|
||||
case SourceRow: {
|
||||
updateSource(currentRow);
|
||||
} break;
|
||||
|
||||
default: qWarning() << "ConnectionModel::handleDataChanged column" << currentColumn;
|
||||
}
|
||||
|
||||
m_lock = false;
|
||||
}
|
||||
|
||||
ConnectionView *ConnectionModel::connectionView() const
|
||||
{
|
||||
return m_connectionView;
|
||||
}
|
||||
|
||||
QStringList ConnectionModel::getSignalsForRow(int row) const
|
||||
{
|
||||
QStringList stringList;
|
||||
SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(row);
|
||||
|
||||
if (signalHandlerProperty.isValid()) {
|
||||
stringList.append(getPossibleSignalsForConnection(signalHandlerProperty.parentModelNode()));
|
||||
}
|
||||
|
||||
return stringList;
|
||||
}
|
||||
|
||||
QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &connection) const
|
||||
{
|
||||
QStringList stringList;
|
||||
|
||||
if (connection.isValid()) {
|
||||
ModelNode targetNode = getTargetNodeForConnection(connection);
|
||||
if (targetNode.isValid() && targetNode.metaInfo().isValid()) {
|
||||
stringList.append(propertyNameListToStringList(targetNode.metaInfo().signalNames()));
|
||||
}
|
||||
}
|
||||
|
||||
return stringList;
|
||||
}
|
||||
|
||||
ConnectionDelegate::ConnectionDelegate(QWidget *parent) : QStyledItemDelegate(parent)
|
||||
{
|
||||
static QItemEditorFactory *factory = 0;
|
||||
if (factory == 0) {
|
||||
factory = new QItemEditorFactory;
|
||||
QItemEditorCreatorBase *creator
|
||||
= new QItemEditorCreator<ConnectionComboBox>("text");
|
||||
factory->registerEditor(QVariant::String, creator);
|
||||
}
|
||||
|
||||
setItemEditorFactory(factory);
|
||||
}
|
||||
|
||||
QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
|
||||
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
|
||||
|
||||
const ConnectionModel *connectionModel = qobject_cast<const ConnectionModel*>(index.model());
|
||||
|
||||
//model->connectionView()->allModelNodes();
|
||||
|
||||
ConnectionComboBox *bindingComboBox = qobject_cast<ConnectionComboBox*>(widget);
|
||||
|
||||
if (!connectionModel) {
|
||||
qWarning() << "ConnectionDelegate::createEditor no model";
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!connectionModel->connectionView()) {
|
||||
qWarning() << "ConnectionDelegate::createEditor no connection view";
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!bindingComboBox) {
|
||||
qWarning() << "ConnectionDelegate::createEditor no bindingComboBox";
|
||||
return widget;
|
||||
}
|
||||
|
||||
switch (index.column()) {
|
||||
case TargetModelNodeRow: {
|
||||
foreach (const ModelNode &modelNode, connectionModel->connectionView()->allModelNodes()) {
|
||||
if (!modelNode.id().isEmpty()) {
|
||||
bindingComboBox->addItem(modelNode.id());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TargetPropertyNameRow: {
|
||||
bindingComboBox->addItems(prependOnForSignalHandler(connectionModel->getSignalsForRow(index.row())));
|
||||
} break;
|
||||
case SourceRow: {
|
||||
ModelNode rootModelNode = connectionModel->connectionView()->rootModelNode();
|
||||
if (QmlItemNode::isValidQmlItemNode(rootModelNode) && !rootModelNode.id().isEmpty()) {
|
||||
|
||||
QString itemText = tr("Change to default state");
|
||||
QString source = QString::fromLatin1("{ %1.state = \"\" }").arg(rootModelNode.id());
|
||||
bindingComboBox->addItem(itemText, source);
|
||||
|
||||
foreach (const QmlModelState &state, QmlItemNode(rootModelNode).states().allStates()) {
|
||||
QString itemText = tr("Change state to %1").arg(state.name());
|
||||
QString source = QString::fromLatin1("{ %1.state = \"%2\" }").arg(rootModelNode.id()).arg(state.name());
|
||||
bindingComboBox->addItem(itemText, source);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default: qWarning() << "ConnectionDelegate::createEditor column" << index.column();
|
||||
}
|
||||
|
||||
connect(bindingComboBox, SIGNAL(activated(QString)), this, SLOT(emitCommitData(QString)));
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
void ConnectionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.state &= ~QStyle::State_HasFocus;
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
void ConnectionDelegate::emitCommitData(const QString & /*text*/)
|
||||
{
|
||||
ConnectionComboBox *bindingComboBox = qobject_cast<ConnectionComboBox*>(sender());
|
||||
|
||||
emit commitData(bindingComboBox);
|
||||
}
|
||||
|
||||
ConnectionComboBox::ConnectionComboBox(QWidget *parent) : QComboBox(parent)
|
||||
{
|
||||
static QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("windows")));
|
||||
setEditable(true);
|
||||
if (style)
|
||||
setStyle(style.data());
|
||||
}
|
||||
|
||||
QString ConnectionComboBox::text() const
|
||||
{
|
||||
int index = findText(currentText());
|
||||
if (index > -1) {
|
||||
QVariant variantData = itemData(index);
|
||||
if (variantData.isValid())
|
||||
return variantData.toString();
|
||||
}
|
||||
|
||||
return currentText();
|
||||
}
|
||||
|
||||
void ConnectionComboBox::setText(const QString &text)
|
||||
{
|
||||
setEditText(text);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,123 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONNECTIONMODEL_H
|
||||
#define CONNECTIONMODEL_H
|
||||
|
||||
#include <modelnode.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <bindingproperty.h>
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QStandardItemModel>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QComboBox>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class Model;
|
||||
class AbstractView;
|
||||
class ModelNode;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class ConnectionView;
|
||||
|
||||
class ConnectionModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionModel(ConnectionView *parent = 0);
|
||||
void resetModel();
|
||||
SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
|
||||
ConnectionView *connectionView() const;
|
||||
|
||||
QStringList getSignalsForRow(int row) const;
|
||||
ModelNode getTargetNodeForConnection(const ModelNode &connection) const;
|
||||
|
||||
void addConnection();
|
||||
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
void variantPropertyChanged(const VariantProperty &variantProperty);
|
||||
|
||||
void deleteConnectionByRow(int currentRow);
|
||||
|
||||
protected:
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
void addConnection(const ModelNode &modelNode);
|
||||
void addSignalHandler(const SignalHandlerProperty &bindingProperty);
|
||||
void removeModelNode(const ModelNode &modelNode);
|
||||
void removeConnection(const ModelNode &modelNode);
|
||||
void updateSource(int row);
|
||||
void updateSignalName(int rowNumber);
|
||||
void updateTargetNode(int rowNumber);
|
||||
void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
|
||||
QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
|
||||
|
||||
private slots:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
||||
private:
|
||||
ConnectionView *m_connectionView;
|
||||
bool m_lock;
|
||||
QString m_exceptionError;
|
||||
};
|
||||
|
||||
|
||||
class ConnectionDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionDelegate(QWidget *parent = 0);
|
||||
|
||||
virtual QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void emitCommitData(const QString &text);
|
||||
};
|
||||
|
||||
class ConnectionComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText USER true)
|
||||
public:
|
||||
ConnectionComboBox(QWidget *parent = 0);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // CONNECTIONMODEL_H
|
@@ -0,0 +1,198 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "connectionview.h"
|
||||
#include "connectionviewwidget.h"
|
||||
|
||||
#include "bindingmodel.h"
|
||||
#include "connectionmodel.h"
|
||||
#include "dynamicpropertiesmodel.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
ConnectionView::ConnectionView(QObject *parent) : AbstractView(parent),
|
||||
m_connectionViewWidget(new ConnectionViewWidget()),
|
||||
m_connectionModel(new ConnectionModel(this)),
|
||||
m_bindingModel(new BindingModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(this))
|
||||
{
|
||||
connectionViewWidget()->setBindingModel(m_bindingModel);
|
||||
connectionViewWidget()->setConnectionModel(m_connectionModel);
|
||||
connectionViewWidget()->setDynamicPropertiesModelModel(m_dynamicPropertiesModel);
|
||||
}
|
||||
|
||||
ConnectionView::~ConnectionView()
|
||||
{
|
||||
}
|
||||
|
||||
void ConnectionView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
}
|
||||
|
||||
void ConnectionView::modelAboutToBeDetached(Model *model)
|
||||
{
|
||||
AbstractView::modelAboutToBeDetached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
}
|
||||
|
||||
void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/)
|
||||
{
|
||||
//bindings
|
||||
connectionModel()->resetModel();
|
||||
}
|
||||
|
||||
void ConnectionView::nodeRemoved(const ModelNode & /*removedNode*/,
|
||||
const NodeAbstractProperty & /*parentProperty*/,
|
||||
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
connectionModel()->resetModel();
|
||||
}
|
||||
|
||||
void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstractProperty & /*newPropertyParent*/,
|
||||
const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
connectionModel()->resetModel();
|
||||
}
|
||||
|
||||
void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/)
|
||||
{
|
||||
connectionModel()->resetModel();
|
||||
}
|
||||
|
||||
void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> & propertyList)
|
||||
{
|
||||
foreach (const AbstractProperty &property, propertyList) {
|
||||
if (property.isBindingProperty()) {
|
||||
bindingModel()->bindingRemoved(property.toBindingProperty());
|
||||
dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty());
|
||||
} else if (property.isVariantProperty()) {
|
||||
//### dynamicPropertiesModel->bindingRemoved(property.toVariantProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
||||
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
foreach (const VariantProperty &variantProperty, propertyList) {
|
||||
if (variantProperty.isDynamic())
|
||||
dynamicPropertiesModel()->variantPropertyChanged(variantProperty);
|
||||
|
||||
connectionModel()->variantPropertyChanged(variantProperty);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConnectionView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
|
||||
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
foreach (const BindingProperty &bindingProperty, propertyList) {
|
||||
bindingModel()->bindingChanged(bindingProperty);
|
||||
if (bindingProperty.isDynamic())
|
||||
dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty);
|
||||
|
||||
connectionModel()->bindingPropertyChanged(bindingProperty);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeList,
|
||||
const QList<ModelNode> & /*lastSelectedNodeList*/)
|
||||
{
|
||||
bindingModel()->selectionChanged(selectedNodeList);
|
||||
dynamicPropertiesModel()->selectionChanged(selectedNodeList);
|
||||
connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex());
|
||||
connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex());
|
||||
|
||||
if (connectionViewWidget()->currentTab() == ConnectionViewWidget::BindingTab
|
||||
|| connectionViewWidget()->currentTab() == ConnectionViewWidget::DynamicPropertiesTab)
|
||||
connectionViewWidget()->setEnabledAddButton(selectedNodeList.count() == 1);
|
||||
}
|
||||
|
||||
WidgetInfo ConnectionView::widgetInfo()
|
||||
{
|
||||
return createWidgetInfo(m_connectionViewWidget.data(),
|
||||
new WidgetInfo::ToolBarWidgetDefaultFactory<ConnectionViewWidget>(connectionViewWidget()),
|
||||
QLatin1String("ConnectionView"),
|
||||
WidgetInfo::LeftPane,
|
||||
0,
|
||||
tr("Connection View"));
|
||||
}
|
||||
|
||||
bool ConnectionView::hasWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QTableView *ConnectionView::connectionTableView() const
|
||||
{
|
||||
return connectionViewWidget()->connectionTableView();
|
||||
}
|
||||
|
||||
QTableView *ConnectionView::bindingTableView() const
|
||||
{
|
||||
return connectionViewWidget()->bindingTableView();
|
||||
}
|
||||
|
||||
QTableView *ConnectionView::dynamicPropertiesTableView() const
|
||||
{
|
||||
return connectionViewWidget()->dynamicPropertiesTableView();
|
||||
}
|
||||
|
||||
ConnectionViewWidget *ConnectionView::connectionViewWidget() const
|
||||
{
|
||||
return m_connectionViewWidget.data();
|
||||
}
|
||||
|
||||
ConnectionModel *ConnectionView::connectionModel() const
|
||||
{
|
||||
return m_connectionModel;
|
||||
}
|
||||
|
||||
BindingModel *ConnectionView::bindingModel() const
|
||||
{
|
||||
return m_bindingModel;
|
||||
}
|
||||
|
||||
DynamicPropertiesModel *ConnectionView::dynamicPropertiesModel() const
|
||||
{
|
||||
return m_dynamicPropertiesModel;
|
||||
}
|
||||
|
||||
} // namesapce Internal
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONNECTIONVIEW_H
|
||||
#define CONNECTIONVIEW_H
|
||||
|
||||
#include <abstractview.h>
|
||||
#include <qmlitemnode.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTableView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class ConnectionViewWidget;
|
||||
class BindingModel;
|
||||
class ConnectionModel;
|
||||
class DynamicPropertiesModel;
|
||||
|
||||
class ConnectionView : public AbstractView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionView(QObject *parent = 0);
|
||||
~ConnectionView();
|
||||
|
||||
// AbstractView
|
||||
void modelAttached(Model *model) override;
|
||||
void modelAboutToBeDetached(Model *model) override;
|
||||
|
||||
void nodeCreated(const ModelNode &createdNode) override;
|
||||
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override;
|
||||
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
|
||||
const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override;
|
||||
void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override;
|
||||
void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) override;
|
||||
void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override;
|
||||
void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) override;
|
||||
|
||||
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
||||
const QList<ModelNode> &lastSelectedNodeList) override;
|
||||
|
||||
WidgetInfo widgetInfo() override;
|
||||
bool hasWidget() const override;
|
||||
|
||||
QTableView *connectionTableView() const;
|
||||
QTableView *bindingTableView() const;
|
||||
QTableView *dynamicPropertiesTableView() const;
|
||||
|
||||
protected:
|
||||
ConnectionViewWidget *connectionViewWidget() const;
|
||||
ConnectionModel *connectionModel() const;
|
||||
BindingModel *bindingModel() const;
|
||||
DynamicPropertiesModel *dynamicPropertiesModel() const;
|
||||
|
||||
|
||||
private: //variables
|
||||
QPointer<ConnectionViewWidget> m_connectionViewWidget;
|
||||
ConnectionModel *m_connectionModel;
|
||||
BindingModel *m_bindingModel;
|
||||
DynamicPropertiesModel *m_dynamicPropertiesModel;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif //CONNECTIONVIEW_H
|
@@ -0,0 +1,287 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "connectionviewwidget.h"
|
||||
#include "connectionview.h"
|
||||
#include "ui_connectionviewwidget.h"
|
||||
|
||||
#include "bindingmodel.h"
|
||||
#include "connectionmodel.h"
|
||||
#include "dynamicpropertiesmodel.h"
|
||||
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QToolButton>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
ui(new Ui::ConnectionViewWidget)
|
||||
{
|
||||
|
||||
setWindowTitle(tr("Connections", "Title of connection view"));
|
||||
ui->setupUi(this);
|
||||
|
||||
setStyleSheet(QLatin1String(Utils::FileReader::fetchQrc(QLatin1String(":/connectionview/stylesheet.css"))));
|
||||
|
||||
//ui->tabWidget->tabBar()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
ui->tabBar->addTab(tr("Connections", "Title of connection view"));
|
||||
ui->tabBar->addTab(tr("Bindings", "Title of connection view"));
|
||||
ui->tabBar->addTab(tr("Dynamic Properties", "Title of dynamic properties view"));
|
||||
ui->tabBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
ui->connectionView->setStyleSheet(
|
||||
QLatin1String(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))));
|
||||
|
||||
ui->bindingView->setStyleSheet(
|
||||
QLatin1String(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))));
|
||||
|
||||
connect(ui->tabBar, SIGNAL(currentChanged(int)),
|
||||
ui->stackedWidget, SLOT(setCurrentIndex(int)));
|
||||
|
||||
connect(ui->tabBar, SIGNAL(currentChanged(int)),
|
||||
this, SLOT(handleTabChanged(int)));
|
||||
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
ConnectionViewWidget::~ConnectionViewWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::setBindingModel(BindingModel *model)
|
||||
{
|
||||
ui->bindingView->setModel(model);
|
||||
ui->bindingView->verticalHeader()->hide();
|
||||
ui->bindingView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->bindingView->setItemDelegate(new BindingDelegate);
|
||||
connect(ui->bindingView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(bindingTableViewSelectionChanged(QModelIndex,QModelIndex)));
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::setConnectionModel(ConnectionModel *model)
|
||||
{
|
||||
ui->connectionView->setModel(model);
|
||||
ui->connectionView->verticalHeader()->hide();
|
||||
ui->connectionView->horizontalHeader()->setDefaultSectionSize(160);
|
||||
ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->connectionView->setItemDelegate(new ConnectionDelegate);
|
||||
connect(ui->connectionView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(connectionTableViewSelectionChanged(QModelIndex,QModelIndex)));
|
||||
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::setDynamicPropertiesModelModel(DynamicPropertiesModel *model)
|
||||
{
|
||||
ui->dynamicPropertiesView->setModel(model);
|
||||
ui->dynamicPropertiesView->verticalHeader()->hide();
|
||||
ui->dynamicPropertiesView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
ui->dynamicPropertiesView->setItemDelegate(new DynamicPropertiesDelegate);
|
||||
connect(ui->dynamicPropertiesView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||
this, SLOT(dynamicPropertiesTableViewSelectionChanged(QModelIndex,QModelIndex)));
|
||||
}
|
||||
|
||||
QList<QToolButton *> ConnectionViewWidget::createToolBarWidgets()
|
||||
{
|
||||
QList<QToolButton *> buttons;
|
||||
|
||||
buttons << new QToolButton();
|
||||
buttons.last()->setIcon(QIcon(QStringLiteral(":/connectionview/plus.png")));
|
||||
buttons.last()->setToolTip(tr("Add binding or connection"));
|
||||
connect(buttons.last(), SIGNAL(clicked()), this, SLOT(addButtonClicked()));
|
||||
connect(this, SIGNAL(setEnabledAddButtonChanged(bool)), buttons.last(), SLOT(setEnabled(bool)));
|
||||
|
||||
buttons << new QToolButton();
|
||||
buttons.last()->setIcon(QIcon(QStringLiteral(":/connectionview/minus.png")));
|
||||
buttons.last()->setToolTip(tr("Remove selected binding or connection"));
|
||||
buttons.last()->setShortcut(QKeySequence(Qt::Key_Delete));
|
||||
connect(buttons.last(), SIGNAL(clicked()), this, SLOT(removeButtonClicked()));
|
||||
connect(this, SIGNAL(setEnabledRemoveButtonChanged(bool)), buttons.last(), SLOT(setEnabled(bool)));
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::setEnabledAddButton(bool enabled)
|
||||
{
|
||||
emit setEnabledAddButtonChanged(enabled);
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::setEnabledRemoveButton(bool enabled)
|
||||
{
|
||||
emit setEnabledRemoveButtonChanged(enabled);
|
||||
}
|
||||
|
||||
ConnectionViewWidget::TabStatus ConnectionViewWidget::currentTab() const
|
||||
{
|
||||
switch (ui->stackedWidget->currentIndex()) {
|
||||
case 0: return ConnectionTab;
|
||||
case 1: return BindingTab;
|
||||
case 2: return DynamicPropertiesTab;
|
||||
default: return InvalidTab;
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::resetItemViews()
|
||||
{
|
||||
if (currentTab() == ConnectionTab) {
|
||||
ui->connectionView->selectionModel()->clear();
|
||||
|
||||
} else if (currentTab() == BindingTab) {
|
||||
ui->bindingView->selectionModel()->clear();
|
||||
|
||||
} else if (currentTab() == DynamicPropertiesTab) {
|
||||
ui->dynamicPropertiesView->selectionModel()->clear();
|
||||
}
|
||||
invalidateButtonStatus();
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::invalidateButtonStatus()
|
||||
{
|
||||
if (currentTab() == ConnectionTab) {
|
||||
setEnabledRemoveButton(ui->connectionView->selectionModel()->hasSelection());
|
||||
setEnabledAddButton(true);
|
||||
} else if (currentTab() == BindingTab) {
|
||||
setEnabledRemoveButton(ui->bindingView->selectionModel()->hasSelection());
|
||||
BindingModel *bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
|
||||
setEnabledAddButton(bindingModel->connectionView()->model() &&
|
||||
bindingModel->connectionView()->selectedModelNodes().count() == 1);
|
||||
|
||||
} else if (currentTab() == DynamicPropertiesTab) {
|
||||
setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection());
|
||||
DynamicPropertiesModel *dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
|
||||
setEnabledAddButton(dynamicPropertiesModel->connectionView()->model() &&
|
||||
dynamicPropertiesModel->connectionView()->selectedModelNodes().count() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
QTableView *ConnectionViewWidget::connectionTableView() const
|
||||
{
|
||||
return ui->connectionView;
|
||||
}
|
||||
|
||||
QTableView *ConnectionViewWidget::bindingTableView() const
|
||||
{
|
||||
return ui->bindingView;
|
||||
}
|
||||
|
||||
QTableView *ConnectionViewWidget::dynamicPropertiesTableView() const
|
||||
{
|
||||
return ui->dynamicPropertiesView;
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::handleTabChanged(int)
|
||||
{
|
||||
invalidateButtonStatus();
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::removeButtonClicked()
|
||||
{
|
||||
if (currentTab() == ConnectionTab) {
|
||||
int currentRow = ui->connectionView->selectionModel()->selectedRows().first().row();
|
||||
ConnectionModel *connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
|
||||
if (connectionModel) {
|
||||
connectionModel->deleteConnectionByRow(currentRow);
|
||||
}
|
||||
} else if (currentTab() == BindingTab) {
|
||||
int currentRow = ui->bindingView->selectionModel()->selectedRows().first().row();
|
||||
BindingModel *bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
|
||||
if (bindingModel) {
|
||||
bindingModel->deleteBindindByRow(currentRow);
|
||||
}
|
||||
} else if (currentTab() == DynamicPropertiesTab) {
|
||||
int currentRow = ui->dynamicPropertiesView->selectionModel()->selectedRows().first().row();
|
||||
DynamicPropertiesModel *dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
|
||||
if (dynamicPropertiesModel)
|
||||
dynamicPropertiesModel->deleteDynamicPropertyByRow(currentRow);
|
||||
}
|
||||
|
||||
invalidateButtonStatus();
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::addButtonClicked()
|
||||
{
|
||||
|
||||
if (currentTab() == ConnectionTab) {
|
||||
ConnectionModel *connectionModel = qobject_cast<ConnectionModel*>(ui->connectionView->model());
|
||||
if (connectionModel) {
|
||||
connectionModel->addConnection();
|
||||
}
|
||||
} else if (currentTab() == BindingTab) {
|
||||
BindingModel *bindingModel = qobject_cast<BindingModel*>(ui->bindingView->model());
|
||||
if (bindingModel) {
|
||||
bindingModel->addBindingForCurrentNode();
|
||||
}
|
||||
|
||||
} else if (currentTab() == DynamicPropertiesTab) {
|
||||
DynamicPropertiesModel *dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
|
||||
if (dynamicPropertiesModel)
|
||||
dynamicPropertiesModel->addDynamicPropertyForCurrentNode();
|
||||
}
|
||||
|
||||
invalidateButtonStatus();
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == BindingTab) {
|
||||
if (current.isValid()) {
|
||||
setEnabledRemoveButton(true);
|
||||
} else {
|
||||
setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == ConnectionTab) {
|
||||
if (current.isValid()) {
|
||||
setEnabledRemoveButton(true);
|
||||
} else {
|
||||
setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||
{
|
||||
if (currentTab() == DynamicPropertiesTab) {
|
||||
if (current.isValid()) {
|
||||
setEnabledRemoveButton(true);
|
||||
} else {
|
||||
setEnabledRemoveButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,103 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONNECTIONVIEWWIDGET_H
|
||||
#define CONNECTIONVIEWWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QAbstractItemView>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QToolButton;
|
||||
class QTableView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Ui { class ConnectionViewWidget; }
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class BindingModel;
|
||||
class ConnectionModel;
|
||||
class DynamicPropertiesModel;
|
||||
|
||||
class ConnectionViewWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum TabStatus {
|
||||
ConnectionTab,
|
||||
BindingTab,
|
||||
DynamicPropertiesTab,
|
||||
InvalidTab
|
||||
};
|
||||
|
||||
explicit ConnectionViewWidget(QWidget *parent = 0);
|
||||
~ConnectionViewWidget();
|
||||
|
||||
void setBindingModel(BindingModel *model);
|
||||
void setConnectionModel(ConnectionModel *model);
|
||||
void setDynamicPropertiesModelModel(DynamicPropertiesModel *model);
|
||||
|
||||
QList<QToolButton*> createToolBarWidgets();
|
||||
|
||||
void setEnabledAddButton(bool enabled);
|
||||
void setEnabledRemoveButton(bool enabled);
|
||||
|
||||
TabStatus currentTab() const;
|
||||
|
||||
void resetItemViews();
|
||||
void invalidateButtonStatus();
|
||||
|
||||
QTableView *connectionTableView() const;
|
||||
QTableView *bindingTableView() const;
|
||||
QTableView *dynamicPropertiesTableView() const;
|
||||
|
||||
public slots:
|
||||
void bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
signals:
|
||||
void setEnabledAddButtonChanged(bool);
|
||||
void setEnabledRemoveButtonChanged(bool);
|
||||
|
||||
private slots:
|
||||
void handleTabChanged(int i);
|
||||
void removeButtonClicked();
|
||||
void addButtonClicked();
|
||||
|
||||
private:
|
||||
Ui::ConnectionViewWidget *ui;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // CONNECTIONVIEWWIDGET_H
|
@@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QmlDesigner::ConnectionViewWidget</class>
|
||||
<widget class="QWidget" name="QmlDesigner::ConnectionViewWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>994</width>
|
||||
<height>611</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Connections</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="widgetSpacer" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>4</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>4</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabBar" name="tabBar" native="true"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="connectionViewPage">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="3" column="0" colspan="5">
|
||||
<widget class="QTableView" name="connectionView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="bindingViewPage">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QTableView" name="bindingView">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="dynamicPropertiesPage">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableView" name="dynamicPropertiesView">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>stackedWidget</zorder>
|
||||
<zorder>tabBar</zorder>
|
||||
<zorder>widgetSpacer</zorder>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QTabBar</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">qtabbar.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@@ -0,0 +1,834 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dynamicpropertiesmodel.h"
|
||||
|
||||
#include "connectionview.h"
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
#include <nodeproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <rewritingexception.h>
|
||||
#include <rewritertransaction.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QItemEditorFactory>
|
||||
#include <QComboBox>
|
||||
#include <QMessageBox>
|
||||
#include <QStyleFactory>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
namespace {
|
||||
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
PropertyNameRow = 1,
|
||||
PropertyTypeRow = 2,
|
||||
PropertyValueRow = 3
|
||||
};
|
||||
|
||||
bool compareBindingProperties(const QmlDesigner::BindingProperty &bindingProperty01, const QmlDesigner::BindingProperty &bindingProperty02)
|
||||
{
|
||||
if (bindingProperty01.parentModelNode() != bindingProperty02.parentModelNode())
|
||||
return false;
|
||||
if (bindingProperty01.name() != bindingProperty02.name())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProperty01, const QmlDesigner::VariantProperty &variantProperty02)
|
||||
{
|
||||
if (variantProperty01.parentModelNode() != variantProperty02.parentModelNode())
|
||||
return false;
|
||||
if (variantProperty01.name() != variantProperty02.name())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
QString idOrTypeNameForNode(const QmlDesigner::ModelNode &modelNode)
|
||||
{
|
||||
QString idLabel = modelNode.id();
|
||||
if (idLabel.isEmpty())
|
||||
idLabel = QString::fromLatin1(modelNode.simplifiedTypeName());
|
||||
|
||||
return idLabel;
|
||||
}
|
||||
|
||||
QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode)
|
||||
{
|
||||
QmlDesigner::PropertyName propertyName = "property";
|
||||
int i = 0;
|
||||
if (modelNode.metaInfo().isValid()) {
|
||||
while (true) {
|
||||
const QmlDesigner::PropertyName currentPropertyName = QString(QString::fromLatin1(propertyName) + QString::number(i)).toLatin1();
|
||||
if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName))
|
||||
return currentPropertyName;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::TypeName &typeName)
|
||||
{
|
||||
QVariant returnValue = variant;
|
||||
|
||||
if (typeName == "int") {
|
||||
bool ok;
|
||||
returnValue = variant.toInt(&ok);
|
||||
if (!ok)
|
||||
returnValue = 0;
|
||||
} else if (typeName == "real") {
|
||||
bool ok;
|
||||
returnValue = variant.toReal(&ok);
|
||||
if (!ok)
|
||||
returnValue = 0.0;
|
||||
|
||||
} else if (typeName == "string") {
|
||||
returnValue = variant.toString();
|
||||
|
||||
} else if (typeName == "bool") {
|
||||
returnValue = variant.toBool();
|
||||
} else if (typeName == "url") {
|
||||
returnValue = variant.toUrl();
|
||||
} else if (typeName == "color") {
|
||||
if (QColor::isValidColor(variant.toString())) {
|
||||
returnValue = variant.toString();
|
||||
} else {
|
||||
returnValue = QColor(Qt::black);
|
||||
}
|
||||
} else if (typeName == "Item") {
|
||||
returnValue = 0;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
} //internal namespace
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(ConnectionView *parent) :
|
||||
QStandardItemModel(parent),
|
||||
m_connectionView(parent),
|
||||
m_lock(false),
|
||||
m_handleDataChanged(false)
|
||||
{
|
||||
connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(handleDataChanged(QModelIndex,QModelIndex)));
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty)
|
||||
{
|
||||
if (!bindingProperty.isDynamic())
|
||||
return;
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
|
||||
if (rowNumber == -1) {
|
||||
addBindingProperty(bindingProperty);
|
||||
} else {
|
||||
updateBindingProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty)
|
||||
{
|
||||
if (!variantProperty.isDynamic())
|
||||
return;
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(variantProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForVariantProperty(variantProperty);
|
||||
|
||||
if (rowNumber == -1) {
|
||||
addVariantProperty(variantProperty);
|
||||
} else {
|
||||
updateVariantProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::selectionChanged(const QList<ModelNode> &selectedNodes)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
m_selectedModelNodes = selectedNodes;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
ConnectionView *DynamicPropertiesModel::connectionView() const
|
||||
{
|
||||
return m_connectionView;
|
||||
}
|
||||
|
||||
BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const
|
||||
{
|
||||
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.bindingProperty(targetPropertyName.toLatin1());
|
||||
|
||||
return BindingProperty();
|
||||
}
|
||||
|
||||
VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const
|
||||
{
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.variantProperty(targetPropertyName.toLatin1());
|
||||
|
||||
return VariantProperty();
|
||||
}
|
||||
|
||||
QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
|
||||
{
|
||||
const ModelNode modelNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (!modelNode.isValid()) {
|
||||
qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
NodeMetaInfo metaInfo = modelNode.metaInfo();
|
||||
|
||||
if (metaInfo.isValid()) {
|
||||
QStringList possibleProperties;
|
||||
foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
|
||||
if (metaInfo.propertyIsWritable(propertyName))
|
||||
possibleProperties << QString::fromLatin1(propertyName);
|
||||
}
|
||||
|
||||
return possibleProperties;
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
ModelNode modelNode = connectionView()->selectedModelNodes().first();
|
||||
if (modelNode.isValid()) {
|
||||
try {
|
||||
modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
|
||||
} catch (RewritingException &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
|
||||
{
|
||||
const QString expression = bindingProperty.expression();
|
||||
const QStringList stringlist = expression.split(QLatin1String("."));
|
||||
|
||||
PropertyName typeName;
|
||||
|
||||
if (bindingProperty.parentModelNode().metaInfo().isValid()) {
|
||||
typeName = bindingProperty.parentModelNode().metaInfo().propertyTypeName(bindingProperty.name());
|
||||
} else {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
|
||||
}
|
||||
|
||||
const QString id = stringlist.first();
|
||||
|
||||
ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
|
||||
|
||||
if (!modelNode.isValid()) {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
NodeMetaInfo metaInfo = modelNode.metaInfo();
|
||||
|
||||
if (metaInfo.isValid()) {
|
||||
QStringList possibleProperties;
|
||||
foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
|
||||
if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check
|
||||
possibleProperties << QString::fromLatin1(propertyName);
|
||||
}
|
||||
|
||||
return possibleProperties;
|
||||
} else {
|
||||
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
if (bindingProperty.isValid()) {
|
||||
bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
}
|
||||
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
clear();
|
||||
|
||||
QStringList labels;
|
||||
|
||||
labels << tr("Item");
|
||||
labels <<tr("Property");
|
||||
labels <<tr("Property Type");
|
||||
labels <<tr("Property Value");
|
||||
|
||||
setHorizontalHeaderLabels(labels);
|
||||
|
||||
foreach (const ModelNode modelNode, m_selectedModelNodes) {
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
|
||||
const QString &propertyType,
|
||||
const AbstractProperty &abstractProperty)
|
||||
{
|
||||
QList<QStandardItem*> items;
|
||||
|
||||
QStandardItem *idItem;
|
||||
QStandardItem *propertyNameItem;
|
||||
QStandardItem *propertyTypeItem;
|
||||
QStandardItem *propertyValueItem;
|
||||
|
||||
idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode()));
|
||||
updateCustomData(idItem, abstractProperty);
|
||||
|
||||
propertyNameItem = new QStandardItem(QString::fromLatin1(abstractProperty.name()));
|
||||
|
||||
items.append(idItem);
|
||||
items.append(propertyNameItem);
|
||||
|
||||
|
||||
propertyTypeItem = new QStandardItem(propertyType);
|
||||
items.append(propertyTypeItem);
|
||||
|
||||
propertyValueItem = new QStandardItem();
|
||||
propertyValueItem->setData(propertyValue, Qt::DisplayRole);
|
||||
items.append(propertyValueItem);
|
||||
|
||||
appendRow(items);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::addBindingProperty(const BindingProperty &property)
|
||||
{
|
||||
QVariant value = property.expression();
|
||||
QString type = QString::fromLatin1(property.dynamicTypeName());
|
||||
addProperty(value, type, property);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::addVariantProperty(const VariantProperty &property)
|
||||
{
|
||||
QVariant value = property.value();
|
||||
QString type = QString::fromLatin1(property.dynamicTypeName());
|
||||
addProperty(value, type, property);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
QString propertyName = QString::fromLatin1(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QString value = bindingProperty.expression();
|
||||
QString type = QString::fromLatin1(bindingProperty.dynamicTypeName());
|
||||
updateDisplayRole(rowNumber, PropertyTypeRow, type);
|
||||
updateDisplayRole(rowNumber, PropertyValueRow, value);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
|
||||
{
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
QString propertyName = QString::fromLatin1(variantProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QVariant value = variantProperty.value();
|
||||
QString type = QString::fromLatin1(variantProperty.dynamicTypeName());
|
||||
updateDisplayRole(rowNumber, PropertyTypeRow, type);
|
||||
|
||||
updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
foreach (const BindingProperty &bindingProperty, modelNode.bindingProperties()) {
|
||||
if (bindingProperty.isDynamic())
|
||||
addBindingProperty(bindingProperty);
|
||||
}
|
||||
|
||||
foreach (const VariantProperty &variantProperty, modelNode.variantProperties()) {
|
||||
if (variantProperty.isDynamic())
|
||||
addVariantProperty(variantProperty);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateValue(int row)
|
||||
{
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(row);
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
|
||||
const QString sourceNode = data(index(row, PropertyTypeRow)).toString();
|
||||
const QString sourceProperty = data(index(row, PropertyValueRow)).toString();
|
||||
|
||||
const QString expression = data(index(row, PropertyValueRow)).toString();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(row);
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = data(index(row, PropertyValueRow));
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, SLOT(handleException()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updatePropertyName(int rowNumber)
|
||||
{
|
||||
const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toLatin1();
|
||||
if (newName.isEmpty()) {
|
||||
qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property name";
|
||||
return;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updatePropertyName"));
|
||||
try {
|
||||
targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
updateCustomData(rowNumber, targetNode.bindingProperty(newName));
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = variantProperty.value();
|
||||
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updatePropertyName"));
|
||||
try {
|
||||
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
updateCustomData(rowNumber, targetNode.variantProperty(newName));
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
{
|
||||
|
||||
const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1();
|
||||
|
||||
if (newType.isEmpty()) {
|
||||
qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property type";
|
||||
return;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName propertyName = bindingProperty.name();
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updatePropertyType"));
|
||||
try {
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
updateCustomData(rowNumber, targetNode.bindingProperty(propertyName));
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = variantProperty.value();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
const PropertyName propertyName = variantProperty.name();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updatePropertyType"));
|
||||
try {
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
if (newType == "alias") { //alias properties have to be bindings
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none"));
|
||||
} else {
|
||||
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType));
|
||||
}
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) { //better save then sorry
|
||||
QMessageBox::warning(0, tr("Error"), e.description());
|
||||
}
|
||||
|
||||
updateCustomData(rowNumber, targetNode.variantProperty(propertyName));
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
updateVariantProperty(rowNumber);
|
||||
} else if (bindingProperty.isBindingProperty()) {
|
||||
updateBindingProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
|
||||
{
|
||||
ModelNode modelNode;
|
||||
|
||||
if (id != QLatin1String("parent")) {
|
||||
modelNode = connectionView()->modelNodeForId(id);
|
||||
} else {
|
||||
if (targetNode.hasParentProperty()) {
|
||||
modelNode = targetNode.parentProperty().parentModelNode();
|
||||
}
|
||||
}
|
||||
return modelNode;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property)
|
||||
{
|
||||
item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(property.name(), Qt::UserRole + 2);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
|
||||
{
|
||||
QStandardItem* idItem = item(row, 0);
|
||||
updateCustomData(idItem, property);
|
||||
}
|
||||
|
||||
int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const
|
||||
{
|
||||
for (int i=0; i < rowCount(); i++) {
|
||||
if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
|
||||
return i;
|
||||
}
|
||||
//not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const
|
||||
{
|
||||
for (int i=0; i < rowCount(); i++) {
|
||||
if (compareVariantProperties(variantPropertyForRow(i), variantProperty))
|
||||
return i;
|
||||
}
|
||||
//not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
|
||||
{
|
||||
//### todo we assume no expressions yet
|
||||
|
||||
const QString expression = bindingProperty.expression();
|
||||
|
||||
if (true) {
|
||||
const QStringList stringList = expression.split(QLatin1String("."));
|
||||
|
||||
*sourceNode = stringList.first();
|
||||
|
||||
QString propertyName;
|
||||
|
||||
for (int i=1; i < stringList.count(); i++) {
|
||||
propertyName += stringList.at(i);
|
||||
if (i != stringList.count() - 1)
|
||||
propertyName += QLatin1String(".");
|
||||
}
|
||||
*sourceProperty = propertyName;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateDisplayRole(int row, int columns, const QString &string)
|
||||
{
|
||||
QModelIndex modelIndex = index(row, columns);
|
||||
if (data(modelIndex).toString() != string)
|
||||
setData(modelIndex, string);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant)
|
||||
{
|
||||
QModelIndex modelIndex = index(row, columns);
|
||||
if (data(modelIndex) != variant)
|
||||
setData(modelIndex, variant);
|
||||
}
|
||||
|
||||
|
||||
void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
if (!m_handleDataChanged)
|
||||
return;
|
||||
|
||||
if (topLeft != bottomRight) {
|
||||
qWarning() << "BindingModel::handleDataChanged multi edit?";
|
||||
return;
|
||||
}
|
||||
|
||||
m_lock = true;
|
||||
|
||||
int currentColumn = topLeft.column();
|
||||
int currentRow = topLeft.row();
|
||||
|
||||
switch (currentColumn) {
|
||||
case TargetModelNodeRow: {
|
||||
//updating user data
|
||||
} break;
|
||||
case PropertyNameRow: {
|
||||
updatePropertyName(currentRow);
|
||||
} break;
|
||||
case PropertyTypeRow: {
|
||||
updatePropertyType(currentRow);
|
||||
} break;
|
||||
case PropertyValueRow: {
|
||||
updateValue(currentRow);
|
||||
} break;
|
||||
|
||||
default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
|
||||
}
|
||||
|
||||
m_lock = false;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::handleException()
|
||||
{
|
||||
QMessageBox::warning(0, tr("Error"), m_exceptionError);
|
||||
resetModel();
|
||||
}
|
||||
|
||||
DynamicPropertiesDelegate::DynamicPropertiesDelegate(QWidget *parent) : QStyledItemDelegate(parent)
|
||||
{
|
||||
// static QItemEditorFactory *factory = 0;
|
||||
// if (factory == 0) {
|
||||
// factory = new QItemEditorFactory;
|
||||
// QItemEditorCreatorBase *creator
|
||||
// = new QItemEditorCreator<DynamicPropertiesComboBox>("text");
|
||||
// factory->registerEditor(QVariant::String, creator);
|
||||
// }
|
||||
|
||||
// setItemEditorFactory(factory);
|
||||
}
|
||||
|
||||
QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index);
|
||||
|
||||
if (widget) {
|
||||
static QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("windows")));
|
||||
if (style)
|
||||
widget->setStyle(style.data());
|
||||
}
|
||||
|
||||
const DynamicPropertiesModel *model = qobject_cast<const DynamicPropertiesModel*>(index.model());
|
||||
|
||||
model->connectionView()->allModelNodes();
|
||||
|
||||
// DynamicPropertiesComboBox *bindingComboBox = qobject_cast<DynamicPropertiesComboBox*>(widget);
|
||||
|
||||
// if (!bindingComboBox) {
|
||||
// return widget;
|
||||
// }
|
||||
|
||||
if (!model) {
|
||||
qWarning() << "BindingDelegate::createEditor no model";
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!model->connectionView()) {
|
||||
qWarning() << "BindingDelegate::createEditor no connection view";
|
||||
return widget;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(index.row());
|
||||
|
||||
switch (index.column()) {
|
||||
case TargetModelNodeRow: {
|
||||
return 0; //no editor
|
||||
} break;
|
||||
case PropertyNameRow: {
|
||||
return QStyledItemDelegate::createEditor(parent, option, index);
|
||||
} break;
|
||||
case PropertyTypeRow: {
|
||||
|
||||
DynamicPropertiesComboBox *bindingComboBox = new DynamicPropertiesComboBox(parent);
|
||||
connect(bindingComboBox, SIGNAL(activated(QString)), this, SLOT(emitCommitData(QString)));
|
||||
|
||||
//bindingComboBox->addItem(QLatin1String("alias"));
|
||||
//bindingComboBox->addItem(QLatin1String("Item"));
|
||||
bindingComboBox->addItem(QLatin1String("real"));
|
||||
bindingComboBox->addItem(QLatin1String("int"));
|
||||
bindingComboBox->addItem(QLatin1String("string"));
|
||||
bindingComboBox->addItem(QLatin1String("bool"));
|
||||
bindingComboBox->addItem(QLatin1String("url"));
|
||||
bindingComboBox->addItem(QLatin1String("color"));
|
||||
bindingComboBox->addItem(QLatin1String("variant"));
|
||||
return bindingComboBox;
|
||||
} break;
|
||||
case PropertyValueRow: {
|
||||
return QStyledItemDelegate::createEditor(parent, option, index);
|
||||
} break;
|
||||
default: qWarning() << "BindingDelegate::createEditor column" << index.column();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DynamicPropertiesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.state &= ~QStyle::State_HasFocus;
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
void DynamicPropertiesDelegate::emitCommitData(const QString & /*text*/)
|
||||
{
|
||||
DynamicPropertiesComboBox *bindingComboBox = qobject_cast<DynamicPropertiesComboBox*>(sender());
|
||||
emit commitData(bindingComboBox);
|
||||
}
|
||||
|
||||
DynamicPropertiesComboBox::DynamicPropertiesComboBox(QWidget *parent) : QComboBox(parent)
|
||||
{
|
||||
static QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("windows")));
|
||||
setEditable(true);
|
||||
if (style)
|
||||
setStyle(style.data());
|
||||
}
|
||||
|
||||
QString DynamicPropertiesComboBox::text() const
|
||||
{
|
||||
return currentText();
|
||||
}
|
||||
|
||||
void DynamicPropertiesComboBox::setText(const QString &text)
|
||||
{
|
||||
setEditText(text);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,136 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef DYNCAMICPROPERTIESMODEL_H
|
||||
#define DYNCAMICPROPERTIESMODEL_H
|
||||
|
||||
#include <modelnode.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QStandardItemModel>
|
||||
#include <QComboBox>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class Model;
|
||||
class ModelNode;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class ConnectionView;
|
||||
|
||||
class DynamicPropertiesModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DynamicPropertiesModel(ConnectionView *parent = 0);
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
void variantPropertyChanged(const VariantProperty &variantProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
void selectionChanged(const QList<ModelNode> &selectedNodes);
|
||||
|
||||
ConnectionView *connectionView() const;
|
||||
BindingProperty bindingPropertyForRow(int rowNumber) const;
|
||||
VariantProperty variantPropertyForRow(int rowNumber) const;
|
||||
QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const;
|
||||
QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const;
|
||||
void deleteDynamicPropertyByRow(int rowNumber);
|
||||
|
||||
void updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant);
|
||||
void addDynamicPropertyForCurrentNode();
|
||||
protected:
|
||||
void resetModel();
|
||||
void addProperty(const QVariant &propertyValue,
|
||||
const QString &propertyType,
|
||||
const AbstractProperty &abstractProperty);
|
||||
void addBindingProperty(const BindingProperty &property);
|
||||
void addVariantProperty(const VariantProperty &property);
|
||||
void updateBindingProperty(int rowNumber);
|
||||
void updateVariantProperty(int rowNumber);
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
void updateValue(int row);
|
||||
void updatePropertyName(int rowNumber);
|
||||
void updatePropertyType(int rowNumber);
|
||||
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
|
||||
void updateCustomData(QStandardItem *item, const AbstractProperty &property);
|
||||
void updateCustomData(int row, const AbstractProperty &property);
|
||||
int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
|
||||
int findRowForVariantProperty(const VariantProperty &variantProperty) const;
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
private slots:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
||||
private:
|
||||
QList<ModelNode> m_selectedModelNodes;
|
||||
ConnectionView *m_connectionView;
|
||||
bool m_lock;
|
||||
bool m_handleDataChanged;
|
||||
QString m_exceptionError;
|
||||
|
||||
};
|
||||
|
||||
class DynamicPropertiesDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DynamicPropertiesDelegate(QWidget *parent = 0);
|
||||
|
||||
virtual QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void emitCommitData(const QString &text);
|
||||
};
|
||||
|
||||
class DynamicPropertiesComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText USER true)
|
||||
public:
|
||||
DynamicPropertiesComboBox(QWidget *parent = 0);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // DYNCAMICPROPERTIESMODEL_Hs
|
Binary file not shown.
After Width: | Height: | Size: 189 B |
Binary file not shown.
After Width: | Height: | Size: 416 B |
Binary file not shown.
After Width: | Height: | Size: 257 B |
Binary file not shown.
After Width: | Height: | Size: 197 B |
@@ -0,0 +1,318 @@
|
||||
QFrame
|
||||
{
|
||||
background-color: #4f4f4f;
|
||||
font-size: 11px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
padding: 0px;
|
||||
font-size: 11px;
|
||||
color: #dedede;
|
||||
}
|
||||
|
||||
QPushButton, QComboBox[editable="false"],
|
||||
QComboBox[editable="true"] {
|
||||
border-image: url(:/qmldesigner/images/combobox-normal.png) 4;
|
||||
border-width: 3;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QPushButton:hover, QComboBox[editable="false"]:hover,
|
||||
QComboBox[editable="true"]:hover, QMenuBar::item:hover {
|
||||
border-image: url(:/qmldesigner/images/combobox-normal.png) 4;
|
||||
border-width: 3;
|
||||
}
|
||||
|
||||
QPushButton:pressed, QComboBox[editable="false"]:on,
|
||||
QComboBox[editable="true"]:on, QMenuBar::item:on {
|
||||
border-image: url(:/qmldesigner/images/combobox-pressed.png) 4;
|
||||
border-width: 3;
|
||||
}
|
||||
|
||||
QComboBox
|
||||
{
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
QComboBox[editable="true"] {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
QComboBox[editable="true"]::drop-down {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: top right;
|
||||
width: 13px;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
QComboBox[editable="true"]::drop-down,
|
||||
QComboBox[editable="true"]::drop-down:hover,
|
||||
QComboBox[editable="true"]::drop-down:on {
|
||||
border-width: 0px;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
QComboBox[editable="true"]::down-arrow:on {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
QComboBox::disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView {
|
||||
show-decoration-selected: 1;
|
||||
background-color: #494949;
|
||||
border: 1px solid black;
|
||||
margin: 0px; /* some spacing around the menu */
|
||||
color: #cacaca;
|
||||
selection-background-color: #d2d2d2;
|
||||
selection-color: #404040;
|
||||
}
|
||||
|
||||
QTableView {
|
||||
color: white;
|
||||
}
|
||||
|
||||
QTableView::item
|
||||
{
|
||||
border: 0px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
QTableView::item:focus
|
||||
{
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QTableView::item:selected
|
||||
{
|
||||
background-color: qlineargradient(x1: 0, y1: -0.4, x2: 0, y2: 0.6,stop: 0 palette(midlight), stop: 1 palette(highlight));
|
||||
|
||||
border: none
|
||||
}
|
||||
|
||||
|
||||
QHeaderView::section {
|
||||
background-color: #494949;
|
||||
padding: 4px;
|
||||
border: 1px solid black;
|
||||
color: #cacaca;
|
||||
border-radius: 2px;
|
||||
margin: 2px
|
||||
}
|
||||
|
||||
QTableView {
|
||||
alternate-background-color: #414141;
|
||||
}
|
||||
|
||||
QWidget#widgetSpacer {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
QStackedWidget {
|
||||
border: 0px;
|
||||
background-color: #4f4f4f;
|
||||
}
|
||||
|
||||
QTabBar::tab:hover {
|
||||
image: url(:/connectionview/panelselected.png);
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
border: none;
|
||||
border-image: none;
|
||||
image: none;
|
||||
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #EFEFEF, stop: 1 #EEEEEE);
|
||||
color: #3c3c3c;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
image: url(:/connectionview/panel.png);
|
||||
|
||||
width: 130px;
|
||||
|
||||
height: 22px;
|
||||
margin-top: 0x;
|
||||
margin-bottom: 0px;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
font: bold;
|
||||
font-size: 11px;
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
QComboBox::menu-indicator, QComboBox::down-arrow {
|
||||
image: url(:/qmldesigner/images/down_arrow.png);
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
QSpinBox
|
||||
{
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 12px;
|
||||
border: 2px solid #0F0F0F;
|
||||
border-radius: 6px;
|
||||
border-width: 1;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #2c2c2c, stop: 1 #333333);
|
||||
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
QDoubleSpinBox
|
||||
{
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
padding-right: 2px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 12px;
|
||||
border: 2px solid #0F0F0F;
|
||||
border-radius: 6px;
|
||||
border-width: 1;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #2c2c2c, stop: 1 #333333);
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
QSpinBox::down-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: bottom right;
|
||||
border-image: url(:/qmldesigner/images/spindown.png) 1;
|
||||
|
||||
width: 16px;
|
||||
border-width: 1px;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
QSpinBox::down-button:hover {
|
||||
border-image: url(:/qmldesigner/images/spindown_hover.png) 1;
|
||||
}
|
||||
|
||||
QSpinBox::down-button:pressed {
|
||||
border-image: url(:/qmldesigner/images/spindown_pressed.png) 1;
|
||||
}
|
||||
|
||||
|
||||
QSpinBox::up-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: top right; /* position at bottom right corner */
|
||||
border-image: url(:/qmldesigner/images/spinup.png) 1;
|
||||
|
||||
width: 16px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
QSpinBox::up-button:hover {
|
||||
border-image: url(:/qmldesigner/images/spinup_hover.png) 1;
|
||||
}
|
||||
|
||||
QSpinBox::up-button:pressed {
|
||||
border-image: url(:/qmldesigner/images/spinup_pressed.png) 1;
|
||||
}
|
||||
|
||||
|
||||
QDoubleSpinBox::down-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: bottom right;
|
||||
border-image: url(:/qmldesigner/images/spindown.png) 1;
|
||||
|
||||
width: 16px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
QDoubleSpinBox::down-button:hover {
|
||||
border-image: url(:/qmldesigner/images/spindown_hover.png) 1;
|
||||
}
|
||||
|
||||
QDoubleSpinBox::down-button:pressed {
|
||||
border-image: url(:/qmldesigner/images/spindown_pressed.png) 1;
|
||||
}
|
||||
|
||||
QDoubleSpinBox::up-button {
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: top right;
|
||||
border-image: url(:/qmldesigner/images/spinup.png) 1;
|
||||
|
||||
width: 16px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
QDoubleSpinBox::up-button:hover {
|
||||
border-image: url(:/qmldesigner/images/spinup_hover.png) 1;
|
||||
}
|
||||
|
||||
QDoubleSpinBox::up-button:pressed {
|
||||
border-image: url(:/qmldesigner/images/spinup_pressed.png) 1;
|
||||
}
|
||||
|
||||
QComboBox[editable="false"]::drop-down {
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right;
|
||||
width: 12px;
|
||||
border-left-style: solid;
|
||||
border-left-color: black;
|
||||
border-left-width: 0px;
|
||||
}
|
||||
|
||||
QComboBox[editable="false"]::down-arrow {
|
||||
subcontrol-origin: content;
|
||||
subcontrol-position: center;
|
||||
position: relative;
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
QComboBox[editable="false"]::down-arrow:on {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
QSpinBox::down-arrow, QSpinBox::menu-indicator, QDoubleSpinBox::down-arrow, QDoubleSpinBox::menu-indicator , QComboBox::menu-indicator, QComboBox::down-arrow {
|
||||
image: url(:/qmldesigner/images/down_arrow.png);
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
QSpinBox::down-arrow:disabled, QSpinBox::down-arrow:off, QDoubleSpinBox::down-arrow:disabled, QDoubleSpinBox::down-arrow:off {
|
||||
image: url(:/qmldesigner/images/down_arrow_disabled.png);
|
||||
}
|
||||
|
||||
QSpinBox::up-arrow, QDoubleSpinBox::up-arrow {
|
||||
image: url(:/qmldesigner/images/up_arrow.png);
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
QSpinBox::up-arrow:disabled, QSpinBox::up-arrow:off, QDoubleSpinBox::up-arrow:disabled, QDoubleSpinBox::up-arrow:off {
|
||||
image: url(:/qmldesigner/images/up_arrow_disabled.png);
|
||||
}
|
||||
|
||||
QLineEdit
|
||||
{
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
border: 2px solid #0F0F0F;
|
||||
border-radius: 6px;
|
||||
border-width: 1;
|
||||
min-height: 26px;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #2c2c2c, stop: 1 #333333);
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "controlpoint.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <rewritertransaction.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
ControlPoint::ControlPoint()
|
||||
{
|
||||
}
|
||||
|
||||
ControlPoint::ControlPoint(const ControlPoint &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
ControlPoint::ControlPoint(const QPointF &coordinate)
|
||||
: d(new ControlPointData)
|
||||
{
|
||||
d->coordinate = coordinate;
|
||||
}
|
||||
|
||||
ControlPoint::ControlPoint(double x, double y)
|
||||
: d(new ControlPointData)
|
||||
{
|
||||
d->coordinate = QPointF(x, y);
|
||||
}
|
||||
|
||||
ControlPoint::~ControlPoint()
|
||||
{
|
||||
}
|
||||
|
||||
ControlPoint &ControlPoint::operator =(const ControlPoint &other)
|
||||
{
|
||||
if (d != other.d)
|
||||
d = other.d;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ControlPoint::setX(double x)
|
||||
{
|
||||
d->coordinate.setX(x);
|
||||
}
|
||||
|
||||
void ControlPoint::setY(double y)
|
||||
{
|
||||
d->coordinate.setY(y);
|
||||
}
|
||||
|
||||
void ControlPoint::setCoordinate(const QPointF &coordinate)
|
||||
{
|
||||
d->coordinate = coordinate;
|
||||
}
|
||||
|
||||
void ControlPoint::setPathElementModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
d->pathElementModelNode = modelNode;
|
||||
}
|
||||
|
||||
ModelNode ControlPoint::pathElementModelNode() const
|
||||
{
|
||||
return d->pathElementModelNode;
|
||||
}
|
||||
|
||||
void ControlPoint::setPathModelNode(const ModelNode &pathModelNode)
|
||||
{
|
||||
d->pathModelNode = pathModelNode;
|
||||
}
|
||||
|
||||
ModelNode ControlPoint::pathModelNode() const
|
||||
{
|
||||
return d->pathModelNode;
|
||||
}
|
||||
|
||||
void ControlPoint::setPointType(PointType pointType)
|
||||
{
|
||||
d->pointType = pointType;
|
||||
}
|
||||
|
||||
PointType ControlPoint::pointType() const
|
||||
{
|
||||
return d->pointType;
|
||||
}
|
||||
|
||||
QPointF ControlPoint::coordinate() const
|
||||
{
|
||||
return d->coordinate;
|
||||
}
|
||||
|
||||
bool ControlPoint::isValid() const
|
||||
{
|
||||
return d.data();
|
||||
}
|
||||
|
||||
bool ControlPoint::isEditPoint() const
|
||||
{
|
||||
return isValid() && (pointType() == StartPoint || pointType() == EndPoint);
|
||||
}
|
||||
|
||||
bool ControlPoint::isControlVertex() const
|
||||
{
|
||||
return isValid() && (pointType() == FirstControlPoint || pointType() == SecondControlPoint);
|
||||
}
|
||||
|
||||
void ControlPoint::updateModelNode()
|
||||
{
|
||||
switch (pointType()) {
|
||||
case StartPoint:
|
||||
d->pathModelNode.variantProperty("startX").setValue(coordinate().x());
|
||||
d->pathModelNode.variantProperty("startY").setValue(coordinate().y());
|
||||
break;
|
||||
case FirstControlPoint:
|
||||
d->pathElementModelNode.variantProperty("control1X").setValue(coordinate().x());
|
||||
d->pathElementModelNode.variantProperty("control1Y").setValue(coordinate().y());
|
||||
break;
|
||||
case SecondControlPoint:
|
||||
d->pathElementModelNode.variantProperty("control2X").setValue(coordinate().x());
|
||||
d->pathElementModelNode.variantProperty("control2Y").setValue(coordinate().y());
|
||||
break;
|
||||
case EndPoint:
|
||||
d->pathElementModelNode.variantProperty("x").setValue(coordinate().x());
|
||||
d->pathElementModelNode.variantProperty("y").setValue(coordinate().y());
|
||||
break;
|
||||
case StartAndEndPoint:
|
||||
d->pathElementModelNode.variantProperty("x").setValue(coordinate().x());
|
||||
d->pathElementModelNode.variantProperty("y").setValue(coordinate().y());
|
||||
d->pathModelNode.variantProperty("startX").setValue(coordinate().x());
|
||||
d->pathModelNode.variantProperty("startY").setValue(coordinate().y());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator ==(const ControlPoint& firstControlPoint, const ControlPoint& secondControlPoint)
|
||||
{
|
||||
return firstControlPoint.d.data() == secondControlPoint.d.data() && firstControlPoint.d.data();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const ControlPoint &controlPoint)
|
||||
{
|
||||
if (controlPoint.isValid()) {
|
||||
debug.nospace() << "ControlPoint("
|
||||
<< controlPoint.coordinate().x() << ", "
|
||||
<< controlPoint.coordinate().y() << ", "
|
||||
<< controlPoint.pointType() << ')';
|
||||
} else {
|
||||
debug.nospace() << "ControlPoint(invalid)";
|
||||
}
|
||||
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,95 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef QMLDESIGNER_CONTROLPOINT_H
|
||||
#define QMLDESIGNER_CONTROLPOINT_H
|
||||
|
||||
#include <modelnode.h>
|
||||
|
||||
#include <QPointF>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
enum PointType {
|
||||
StartPoint,
|
||||
FirstControlPoint,
|
||||
SecondControlPoint,
|
||||
EndPoint,
|
||||
StartAndEndPoint
|
||||
};
|
||||
|
||||
class ControlPointData : public QSharedData
|
||||
{
|
||||
public:
|
||||
ModelNode pathElementModelNode;
|
||||
ModelNode pathModelNode;
|
||||
QPointF coordinate;
|
||||
PointType pointType;
|
||||
};
|
||||
|
||||
class ControlPoint
|
||||
{
|
||||
friend bool operator ==(const ControlPoint& firstControlPoint, const ControlPoint& secondControlPoint);
|
||||
|
||||
public:
|
||||
ControlPoint();
|
||||
ControlPoint(const ControlPoint &other);
|
||||
ControlPoint(const QPointF &coordinate);
|
||||
ControlPoint(double x, double y);
|
||||
|
||||
~ControlPoint();
|
||||
|
||||
ControlPoint &operator =(const ControlPoint &other);
|
||||
|
||||
void setX(double x);
|
||||
void setY(double y);
|
||||
void setCoordinate(const QPointF &coordinate);
|
||||
QPointF coordinate() const;
|
||||
|
||||
void setPathElementModelNode(const ModelNode &pathElementModelNode);
|
||||
ModelNode pathElementModelNode() const;
|
||||
|
||||
void setPathModelNode(const ModelNode &pathModelNode);
|
||||
ModelNode pathModelNode() const;
|
||||
|
||||
void setPointType(PointType pointType);
|
||||
PointType pointType() const;
|
||||
|
||||
bool isValid() const;
|
||||
bool isEditPoint() const;
|
||||
bool isControlVertex() const;
|
||||
|
||||
void updateModelNode();
|
||||
|
||||
private:
|
||||
QExplicitlySharedDataPointer<ControlPointData> d;
|
||||
};
|
||||
|
||||
bool operator ==(const ControlPoint& firstControlPoint, const ControlPoint& secondControlPoint);
|
||||
QDebug operator<<(QDebug debug, const ControlPoint &controlPoint);
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // QMLDESIGNER_CONTROLPOINT_H
|
@@ -0,0 +1,368 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "cubicsegment.h"
|
||||
|
||||
#include <qmath.h>
|
||||
#include <QtDebug>
|
||||
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
CubicSegment::CubicSegment()
|
||||
{
|
||||
}
|
||||
|
||||
CubicSegment CubicSegment::create()
|
||||
{
|
||||
CubicSegment cubicSegment;
|
||||
cubicSegment.d = new CubicSegmentData;
|
||||
|
||||
return cubicSegment;
|
||||
}
|
||||
|
||||
void CubicSegment::setModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
d->modelNode = modelNode;
|
||||
}
|
||||
|
||||
ModelNode CubicSegment::modelNode() const
|
||||
{
|
||||
return d->modelNode;
|
||||
}
|
||||
|
||||
void CubicSegment::setFirstControlPoint(const ControlPoint &firstControlPoint)
|
||||
{
|
||||
d->firstControllPoint = firstControlPoint;
|
||||
}
|
||||
|
||||
void CubicSegment::setFirstControlPoint(double x, double y)
|
||||
{
|
||||
d->firstControllPoint.setX(x);
|
||||
d->firstControllPoint.setY(y);
|
||||
}
|
||||
|
||||
void CubicSegment::setFirstControlPoint(const QPointF &coordiante)
|
||||
{
|
||||
d->firstControllPoint.setCoordinate(coordiante);
|
||||
}
|
||||
|
||||
void CubicSegment::setSecondControlPoint(const ControlPoint &secondControlPoint)
|
||||
{
|
||||
d->secondControllPoint = secondControlPoint;
|
||||
d->secondControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->secondControllPoint.setPointType(FirstControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setSecondControlPoint(double x, double y)
|
||||
{
|
||||
d->secondControllPoint.setX(x);
|
||||
d->secondControllPoint.setY(y);
|
||||
d->secondControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->secondControllPoint.setPointType(FirstControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setSecondControlPoint(const QPointF &coordiante)
|
||||
{
|
||||
d->secondControllPoint.setCoordinate(coordiante);
|
||||
d->secondControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->secondControllPoint.setPointType(FirstControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setThirdControlPoint(const ControlPoint &thirdControlPoint)
|
||||
{
|
||||
d->thirdControllPoint = thirdControlPoint;
|
||||
d->thirdControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->thirdControllPoint.setPointType(SecondControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setThirdControlPoint(double x, double y)
|
||||
{
|
||||
d->thirdControllPoint.setX(x);
|
||||
d->thirdControllPoint.setY(y);
|
||||
d->thirdControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->thirdControllPoint.setPointType(SecondControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setThirdControlPoint(const QPointF &coordiante)
|
||||
{
|
||||
d->thirdControllPoint.setCoordinate(coordiante);
|
||||
d->thirdControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->thirdControllPoint.setPointType(SecondControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setFourthControlPoint(const ControlPoint &fourthControlPoint)
|
||||
{
|
||||
d->fourthControllPoint = fourthControlPoint;
|
||||
d->fourthControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->fourthControllPoint.setPointType(EndPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setFourthControlPoint(double x, double y)
|
||||
{
|
||||
d->fourthControllPoint.setX(x);
|
||||
d->fourthControllPoint.setY(y);
|
||||
d->fourthControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->fourthControllPoint.setPointType(EndPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setFourthControlPoint(const QPointF &coordiante)
|
||||
{
|
||||
d->fourthControllPoint.setCoordinate(coordiante);
|
||||
d->fourthControllPoint.setPathElementModelNode(d->modelNode);
|
||||
d->fourthControllPoint.setPointType(EndPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::setAttributes(const QMap<QString, QVariant> &attributes)
|
||||
{
|
||||
d->attributes = attributes;
|
||||
}
|
||||
|
||||
void CubicSegment::setPercent(double percent)
|
||||
{
|
||||
d->percent = percent;
|
||||
}
|
||||
|
||||
ControlPoint CubicSegment::firstControlPoint() const
|
||||
{
|
||||
return d->firstControllPoint;
|
||||
}
|
||||
|
||||
ControlPoint CubicSegment::secondControlPoint() const
|
||||
{
|
||||
return d->secondControllPoint;
|
||||
}
|
||||
|
||||
ControlPoint CubicSegment::thirdControlPoint() const
|
||||
{
|
||||
return d->thirdControllPoint;
|
||||
}
|
||||
|
||||
ControlPoint CubicSegment::fourthControlPoint() const
|
||||
{
|
||||
return d->fourthControllPoint;
|
||||
}
|
||||
|
||||
const QMap<QString, QVariant> CubicSegment::attributes() const
|
||||
{
|
||||
return d->attributes;
|
||||
}
|
||||
|
||||
double CubicSegment::percent() const
|
||||
{
|
||||
return d->percent;
|
||||
}
|
||||
|
||||
QList<ControlPoint> CubicSegment::controlPoints() const
|
||||
{
|
||||
QList<ControlPoint> controlPointList;
|
||||
|
||||
controlPointList.reserve(4);
|
||||
|
||||
controlPointList.append(firstControlPoint());
|
||||
controlPointList.append(secondControlPoint());
|
||||
controlPointList.append(thirdControlPoint());
|
||||
controlPointList.append(fourthControlPoint());
|
||||
|
||||
return controlPointList;
|
||||
}
|
||||
|
||||
double CubicSegment::firstControlX() const
|
||||
{
|
||||
return firstControlPoint().coordinate().x();
|
||||
}
|
||||
|
||||
double CubicSegment::firstControlY() const
|
||||
{
|
||||
return firstControlPoint().coordinate().y();
|
||||
}
|
||||
|
||||
double CubicSegment::secondControlX() const
|
||||
{
|
||||
return secondControlPoint().coordinate().x();
|
||||
}
|
||||
|
||||
double CubicSegment::secondControlY() const
|
||||
{
|
||||
return secondControlPoint().coordinate().y();
|
||||
}
|
||||
|
||||
double CubicSegment::thirdControlX() const
|
||||
{
|
||||
return thirdControlPoint().coordinate().x();
|
||||
}
|
||||
|
||||
double CubicSegment::thirdControlY() const
|
||||
{
|
||||
return thirdControlPoint().coordinate().y();
|
||||
}
|
||||
|
||||
double CubicSegment::fourthControlX() const
|
||||
{
|
||||
return fourthControlPoint().coordinate().x();
|
||||
}
|
||||
|
||||
double CubicSegment::fourthControlY() const
|
||||
{
|
||||
return fourthControlPoint().coordinate().y();
|
||||
}
|
||||
|
||||
double CubicSegment::quadraticControlX() const
|
||||
{
|
||||
return -0.25 * firstControlX() + 0.75 * secondControlX() + 0.75 * thirdControlX() - 0.25 * fourthControlX();
|
||||
}
|
||||
|
||||
double CubicSegment::quadraticControlY() const
|
||||
{
|
||||
return -0.25 * firstControlY() + 0.75 * secondControlY() + 0.75 * thirdControlY() - 0.25 * fourthControlY();
|
||||
}
|
||||
|
||||
bool CubicSegment::isValid() const
|
||||
{
|
||||
return d.data();
|
||||
}
|
||||
|
||||
bool CubicSegment::canBeConvertedToLine() const
|
||||
{
|
||||
return canBeConvertedToQuad()
|
||||
&& qFuzzyIsNull(((3. * d->firstControllPoint.coordinate())
|
||||
- (6. * d->secondControllPoint.coordinate())
|
||||
+ (3. * d->thirdControllPoint.coordinate())).manhattanLength());;
|
||||
}
|
||||
|
||||
bool CubicSegment::canBeConvertedToQuad() const
|
||||
{
|
||||
return qFuzzyIsNull(((3. * d->secondControllPoint.coordinate())
|
||||
- (3 * d->thirdControllPoint.coordinate())
|
||||
+ d->fourthControllPoint.coordinate()
|
||||
- d->firstControllPoint.coordinate()).manhattanLength());
|
||||
}
|
||||
|
||||
QPointF CubicSegment::sample(double t) const
|
||||
{
|
||||
return qPow(1.-t, 3.) * firstControlPoint().coordinate()
|
||||
+ 3 * qPow(1.-t, 2.) * t * secondControlPoint().coordinate()
|
||||
+ 3 * qPow(t, 2.) * (1. - t) * thirdControlPoint().coordinate()
|
||||
+ qPow(t, 3.) * fourthControlPoint().coordinate();
|
||||
}
|
||||
|
||||
double CubicSegment::minimumDistance(const QPointF &pickPoint, double &tReturnValue) const
|
||||
{
|
||||
double actualMinimumDistance = 10000000.;
|
||||
for (double t = 0.0; t <= 1.0; t += 0.1) {
|
||||
QPointF samplePoint = sample(t);
|
||||
QPointF distanceVector = pickPoint - samplePoint;
|
||||
if (distanceVector.manhattanLength() < actualMinimumDistance) {
|
||||
actualMinimumDistance = distanceVector.manhattanLength();
|
||||
tReturnValue = t;
|
||||
}
|
||||
}
|
||||
|
||||
return actualMinimumDistance;
|
||||
}
|
||||
|
||||
static QPointF interpolatedPoint(double t, const QPointF &firstPoint, const QPointF &secondPoint)
|
||||
{
|
||||
return (secondPoint - firstPoint) * t + firstPoint;
|
||||
}
|
||||
|
||||
QPair<CubicSegment, CubicSegment> CubicSegment::split(double t)
|
||||
{
|
||||
// first pass
|
||||
QPointF secondPointFirstSegment = interpolatedPoint(t, firstControlPoint().coordinate(), secondControlPoint().coordinate());
|
||||
QPointF firstIntermediatPoint = interpolatedPoint(t, secondControlPoint().coordinate(), thirdControlPoint().coordinate());
|
||||
QPointF thirdPointSecondSegment = interpolatedPoint(t, thirdControlPoint().coordinate(), fourthControlPoint().coordinate());
|
||||
|
||||
// second pass
|
||||
QPointF thirdPointFirstSegment = interpolatedPoint(t, secondPointFirstSegment, firstIntermediatPoint);
|
||||
QPointF secondPointSecondSegment = interpolatedPoint(t, firstIntermediatPoint, thirdPointSecondSegment);
|
||||
|
||||
// third pass
|
||||
QPointF midPoint = interpolatedPoint(t, thirdPointFirstSegment, secondPointSecondSegment);
|
||||
ControlPoint midControlPoint(midPoint);
|
||||
|
||||
|
||||
CubicSegment firstCubicSegment = CubicSegment::create();
|
||||
firstCubicSegment.setFirstControlPoint(firstControlPoint().coordinate());
|
||||
firstCubicSegment.setSecondControlPoint(secondPointFirstSegment);
|
||||
firstCubicSegment.setThirdControlPoint(thirdPointFirstSegment);
|
||||
firstCubicSegment.setFourthControlPoint(midControlPoint);
|
||||
|
||||
CubicSegment secondCubicSegment = CubicSegment::create();
|
||||
secondCubicSegment.setFirstControlPoint(midControlPoint);
|
||||
secondCubicSegment.setSecondControlPoint(secondPointSecondSegment);
|
||||
secondCubicSegment.setThirdControlPoint(thirdPointSecondSegment);
|
||||
secondCubicSegment.setFourthControlPoint(fourthControlPoint().coordinate());
|
||||
|
||||
qDebug() << firstCubicSegment << secondCubicSegment;
|
||||
|
||||
return qMakePair(firstCubicSegment, secondCubicSegment);
|
||||
}
|
||||
|
||||
void CubicSegment::makeStraightLine()
|
||||
{
|
||||
QPointF lineVector = fourthControlPoint().coordinate() - firstControlPoint().coordinate();
|
||||
QPointF newSecondControlPoint = firstControlPoint().coordinate() + (lineVector * 0.3);
|
||||
QPointF newThirdControlPoint = fourthControlPoint().coordinate() - (lineVector * 0.3);
|
||||
setSecondControlPoint(newSecondControlPoint);
|
||||
setThirdControlPoint(newThirdControlPoint);
|
||||
}
|
||||
|
||||
void CubicSegment::updateModelNode()
|
||||
{
|
||||
firstControlPoint().updateModelNode();
|
||||
secondControlPoint().updateModelNode();
|
||||
thirdControlPoint().updateModelNode();
|
||||
fourthControlPoint().updateModelNode();
|
||||
}
|
||||
|
||||
CubicSegmentData::CubicSegmentData()
|
||||
: firstControllPoint(0., 0.),
|
||||
secondControllPoint(0., 0.),
|
||||
thirdControllPoint(0., 0.),
|
||||
fourthControllPoint(0., 0.),
|
||||
percent(-1.0)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator ==(const CubicSegment& firstCubicSegment, const CubicSegment& secondCubicSegment)
|
||||
{
|
||||
return firstCubicSegment.d.data() == secondCubicSegment.d.data();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const CubicSegment &cubicSegment)
|
||||
{
|
||||
if (cubicSegment.isValid()) {
|
||||
debug.nospace() << "CubicSegment("
|
||||
<< cubicSegment.firstControlPoint() << ", "
|
||||
<< cubicSegment.secondControlPoint() << ", "
|
||||
<< cubicSegment.thirdControlPoint() << ", "
|
||||
<< cubicSegment.fourthControlPoint() << ')';
|
||||
} else {
|
||||
debug.nospace() << "CubicSegment(invalid)";
|
||||
}
|
||||
|
||||
return debug.space();
|
||||
}
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLDESIGNER_CUBICSEGMENT_H
|
||||
#define QMLDESIGNER_CUBICSEGMENT_H
|
||||
|
||||
#include "controlpoint.h"
|
||||
|
||||
#include <modelnode.h>
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include <QPointF>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
|
||||
class CubicSegmentData : public QSharedData
|
||||
{
|
||||
public:
|
||||
CubicSegmentData();
|
||||
ModelNode modelNode;
|
||||
ControlPoint firstControllPoint;
|
||||
ControlPoint secondControllPoint;
|
||||
ControlPoint thirdControllPoint;
|
||||
ControlPoint fourthControllPoint;
|
||||
QMap<QString, QVariant> attributes;
|
||||
double percent;
|
||||
};
|
||||
|
||||
class CubicSegment
|
||||
{
|
||||
friend bool operator ==(const CubicSegment& firstCubicSegment, const CubicSegment& secondCubicSegment);
|
||||
|
||||
public:
|
||||
CubicSegment();
|
||||
|
||||
static CubicSegment create();
|
||||
|
||||
void setModelNode(const ModelNode &modelNode);
|
||||
ModelNode modelNode() const;
|
||||
|
||||
void setFirstControlPoint(const ControlPoint &firstControlPoint);
|
||||
void setFirstControlPoint(double x, double y);
|
||||
void setFirstControlPoint(const QPointF &coordiante);
|
||||
|
||||
void setSecondControlPoint(const ControlPoint &secondControlPoint);
|
||||
void setSecondControlPoint(double x, double y);
|
||||
void setSecondControlPoint(const QPointF &coordiante);
|
||||
|
||||
void setThirdControlPoint(const ControlPoint &thirdControlPoint);
|
||||
void setThirdControlPoint(double x, double y);
|
||||
void setThirdControlPoint(const QPointF &coordiante);
|
||||
|
||||
void setFourthControlPoint(const ControlPoint &fourthControlPoint);
|
||||
void setFourthControlPoint(double x, double y);
|
||||
void setFourthControlPoint(const QPointF &coordiante);
|
||||
|
||||
void setAttributes(const QMap<QString, QVariant> &attributes);
|
||||
|
||||
void setPercent(double percent);
|
||||
|
||||
ControlPoint firstControlPoint() const;
|
||||
ControlPoint secondControlPoint() const;
|
||||
ControlPoint thirdControlPoint() const;
|
||||
ControlPoint fourthControlPoint() const;
|
||||
|
||||
const QMap<QString, QVariant> attributes() const;
|
||||
|
||||
double percent() const;
|
||||
|
||||
QList<ControlPoint> controlPoints() const;
|
||||
|
||||
double firstControlX() const;
|
||||
double firstControlY() const;
|
||||
double secondControlX() const;
|
||||
double secondControlY() const;
|
||||
double thirdControlX() const;
|
||||
double thirdControlY() const;
|
||||
double fourthControlX() const;
|
||||
double fourthControlY() const;
|
||||
double quadraticControlX() const;
|
||||
double quadraticControlY() const;
|
||||
|
||||
bool isValid() const;
|
||||
bool canBeConvertedToLine() const;
|
||||
bool canBeConvertedToQuad() const;
|
||||
|
||||
QPointF sample(double t) const;
|
||||
double minimumDistance(const QPointF &pickPoint, double &t) const;
|
||||
|
||||
QPair<CubicSegment, CubicSegment> split(double t);
|
||||
|
||||
void makeStraightLine();
|
||||
|
||||
void updateModelNode();
|
||||
|
||||
private:
|
||||
QExplicitlySharedDataPointer<CubicSegmentData> d;
|
||||
};
|
||||
|
||||
bool operator ==(const CubicSegment& firstCubicSegment, const CubicSegment& secondCubicSegment);
|
||||
QDebug operator<<(QDebug debug, const CubicSegment &cubicSegment);
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // QMLDESIGNER_CUBICSEGMENT_H
|
@@ -0,0 +1,970 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pathitem.h"
|
||||
|
||||
#include <nodeproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <nodelistproperty.h>
|
||||
#include <rewritertransaction.h>
|
||||
#include <formeditorscene.h>
|
||||
#include <formeditorview.h>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMenu>
|
||||
#include <QtDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
PathItem::PathItem(FormEditorScene* scene)
|
||||
: QGraphicsObject(),
|
||||
m_selectionManipulator(this),
|
||||
m_lastPercent(-1.),
|
||||
m_formEditorItem(0),
|
||||
m_dontUpdatePath(false)
|
||||
{
|
||||
scene->addItem(this);
|
||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||
}
|
||||
|
||||
PathItem::~PathItem()
|
||||
{
|
||||
m_formEditorItem = 0;
|
||||
}
|
||||
|
||||
static ModelNode pathModelNode(FormEditorItem *formEditorItem)
|
||||
{
|
||||
ModelNode modelNode = formEditorItem->qmlItemNode().modelNode();
|
||||
|
||||
return modelNode.nodeProperty("path").modelNode();
|
||||
}
|
||||
|
||||
typedef QPair<PropertyName, QVariant> PropertyPair;
|
||||
|
||||
void PathItem::writeLinePath(ModelNode pathNode, const CubicSegment &cubicSegment)
|
||||
{
|
||||
QList<PropertyPair> propertyList;
|
||||
propertyList.append(PropertyPair("x", cubicSegment.fourthControlX()));
|
||||
propertyList.append(PropertyPair("y", cubicSegment.fourthControlY()));
|
||||
|
||||
ModelNode lineNode = pathNode.view()->createModelNode("QtQuick.PathLine", pathNode.majorVersion(), pathNode.minorVersion(), propertyList);
|
||||
pathNode.nodeListProperty("pathElements").reparentHere(lineNode);
|
||||
}
|
||||
|
||||
void PathItem::writeQuadPath(ModelNode pathNode, const CubicSegment &cubicSegment)
|
||||
{
|
||||
QList<QPair<PropertyName, QVariant> > propertyList;
|
||||
propertyList.append(PropertyPair("controlX", cubicSegment.quadraticControlX()));
|
||||
propertyList.append(PropertyPair("controlY", cubicSegment.quadraticControlY()));
|
||||
propertyList.append(PropertyPair("x", cubicSegment.fourthControlX()));
|
||||
propertyList.append(PropertyPair("y", cubicSegment.fourthControlY()));
|
||||
|
||||
ModelNode lineNode = pathNode.view()->createModelNode("QtQuick.PathQuad", pathNode.majorVersion(), pathNode.minorVersion(), propertyList);
|
||||
pathNode.nodeListProperty("pathElements").reparentHere(lineNode);
|
||||
}
|
||||
|
||||
void PathItem::writeCubicPath(ModelNode pathNode, const CubicSegment &cubicSegment)
|
||||
{
|
||||
QList<QPair<PropertyName, QVariant> > propertyList;
|
||||
propertyList.append(PropertyPair("control1X", cubicSegment.secondControlX()));
|
||||
propertyList.append(PropertyPair("control1Y", cubicSegment.secondControlY()));
|
||||
propertyList.append(PropertyPair("control2X", cubicSegment.thirdControlX()));
|
||||
propertyList.append(PropertyPair("control2Y", cubicSegment.thirdControlY()));
|
||||
propertyList.append(PropertyPair("x", cubicSegment.fourthControlX()));
|
||||
propertyList.append(PropertyPair("y", cubicSegment.fourthControlY()));
|
||||
|
||||
ModelNode lineNode = pathNode.view()->createModelNode("QtQuick.PathCubic", pathNode.majorVersion(), pathNode.minorVersion(), propertyList);
|
||||
pathNode.nodeListProperty("pathElements").reparentHere(lineNode);
|
||||
}
|
||||
|
||||
void PathItem::writePathAttributes(ModelNode pathNode, const QMap<QString, QVariant> &attributes)
|
||||
{
|
||||
QMapIterator<QString, QVariant> attributesIterator(attributes);
|
||||
while (attributesIterator.hasNext()) {
|
||||
attributesIterator.next();
|
||||
QList<QPair<PropertyName, QVariant> > propertyList;
|
||||
propertyList.append(PropertyPair("name", attributesIterator.key()));
|
||||
propertyList.append(PropertyPair("value", attributesIterator.value()));
|
||||
|
||||
ModelNode lineNode = pathNode.view()->createModelNode("QtQuick.PathAttribute", pathNode.majorVersion(), pathNode.minorVersion(), propertyList);
|
||||
pathNode.nodeListProperty("pathElements").reparentHere(lineNode);
|
||||
}
|
||||
}
|
||||
|
||||
void PathItem::writePathPercent(ModelNode pathNode, double percent)
|
||||
{
|
||||
if (percent >= 0.0) {
|
||||
QList<QPair<PropertyName, QVariant> > propertyList;
|
||||
propertyList.append(PropertyPair("value", percent));
|
||||
|
||||
ModelNode lineNode = pathNode.view()->createModelNode("QtQuick.PathPercent", pathNode.majorVersion(), pathNode.minorVersion(), propertyList);
|
||||
pathNode.nodeListProperty("pathElements").reparentHere(lineNode);
|
||||
}
|
||||
}
|
||||
|
||||
void PathItem::writePathToProperty()
|
||||
{
|
||||
PathUpdateDisabler pathUpdateDisable(this);
|
||||
|
||||
ModelNode pathNode = pathModelNode(formEditorItem());
|
||||
|
||||
RewriterTransaction rewriterTransaction = pathNode.view()->beginRewriterTransaction(QByteArrayLiteral("PathItem::writePathToProperty"));
|
||||
|
||||
QList<ModelNode> pathSegmentNodes = pathNode.nodeListProperty("pathElements").toModelNodeList();
|
||||
|
||||
foreach (ModelNode pathSegment, pathSegmentNodes)
|
||||
pathSegment.destroy();
|
||||
|
||||
if (!m_cubicSegments.isEmpty()) {
|
||||
pathNode.variantProperty("startX").setValue(m_cubicSegments.first().firstControlPoint().coordinate().x());
|
||||
pathNode.variantProperty("startY").setValue(m_cubicSegments.first().firstControlPoint().coordinate().y());
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, m_cubicSegments) {
|
||||
writePathAttributes(pathNode, cubicSegment.attributes());
|
||||
writePathPercent(pathNode, cubicSegment.percent());
|
||||
|
||||
if (cubicSegment.canBeConvertedToLine())
|
||||
writeLinePath(pathNode, cubicSegment);
|
||||
else if (cubicSegment.canBeConvertedToQuad())
|
||||
writeQuadPath(pathNode, cubicSegment);
|
||||
else
|
||||
writeCubicPath(pathNode, cubicSegment);
|
||||
}
|
||||
|
||||
writePathAttributes(pathNode, m_lastAttributes);
|
||||
writePathPercent(pathNode, m_lastPercent);
|
||||
}
|
||||
|
||||
rewriterTransaction.commit();
|
||||
}
|
||||
|
||||
void PathItem::writePathAsCubicSegmentsOnly()
|
||||
{
|
||||
PathUpdateDisabler pathUpdateDisabler(this);
|
||||
|
||||
ModelNode pathNode = pathModelNode(formEditorItem());
|
||||
|
||||
RewriterTransaction rewriterTransaction =
|
||||
pathNode.view()->beginRewriterTransaction(QByteArrayLiteral("PathItem::writePathAsCubicSegmentsOnly"));
|
||||
|
||||
QList<ModelNode> pathSegmentNodes = pathNode.nodeListProperty("pathElements").toModelNodeList();
|
||||
|
||||
foreach (ModelNode pathSegment, pathSegmentNodes)
|
||||
pathSegment.destroy();
|
||||
|
||||
if (!m_cubicSegments.isEmpty()) {
|
||||
pathNode.variantProperty("startX").setValue(m_cubicSegments.first().firstControlPoint().coordinate().x());
|
||||
pathNode.variantProperty("startY").setValue(m_cubicSegments.first().firstControlPoint().coordinate().y());
|
||||
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, m_cubicSegments) {
|
||||
writePathAttributes(pathNode, cubicSegment.attributes());
|
||||
writePathPercent(pathNode, cubicSegment.percent());
|
||||
writeCubicPath(pathNode, cubicSegment);
|
||||
}
|
||||
|
||||
writePathAttributes(pathNode, m_lastAttributes);
|
||||
writePathPercent(pathNode, m_lastPercent);
|
||||
}
|
||||
|
||||
rewriterTransaction.commit();
|
||||
}
|
||||
|
||||
void PathItem::setFormEditorItem(FormEditorItem *formEditorItem)
|
||||
{
|
||||
m_formEditorItem = formEditorItem;
|
||||
setTransform(formEditorItem->sceneTransform());
|
||||
updatePath();
|
||||
|
||||
// m_textEdit->setPlainText(m_formEditorItem->qmlItemNode().modelValue("path").toString());
|
||||
}
|
||||
|
||||
static bool hasPath(FormEditorItem *formEditorItem)
|
||||
{
|
||||
ModelNode modelNode = formEditorItem->qmlItemNode().modelNode();
|
||||
|
||||
return modelNode.hasProperty("path") && modelNode.property("path").isNodeProperty();
|
||||
}
|
||||
|
||||
QPointF startPoint(const ModelNode &modelNode)
|
||||
{
|
||||
QPointF point;
|
||||
|
||||
if (modelNode.hasProperty("startX"))
|
||||
point.setX(modelNode.variantProperty("startX").value().toDouble());
|
||||
|
||||
if (modelNode.hasProperty("startY"))
|
||||
point.setY(modelNode.variantProperty("startY").value().toDouble());
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
static void addCubicSegmentToPainterPath(const CubicSegment &cubicSegment, QPainterPath &painterPath)
|
||||
{
|
||||
painterPath.cubicTo(cubicSegment.secondControlPoint().coordinate(),
|
||||
cubicSegment.thirdControlPoint().coordinate(),
|
||||
cubicSegment.fourthControlPoint().coordinate());
|
||||
|
||||
}
|
||||
|
||||
static void drawCubicSegments(const QList<CubicSegment> &cubicSegments, QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
|
||||
QPainterPath curvePainterPath(cubicSegments.first().firstControlPoint().coordinate());
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, cubicSegments)
|
||||
addCubicSegmentToPainterPath(cubicSegment, curvePainterPath);
|
||||
|
||||
painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
|
||||
painter->drawPath(curvePainterPath);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
static void drawControlLine(const CubicSegment &cubicSegment, QPainter *painter)
|
||||
{
|
||||
static QPen solidPen(QColor(104, 183, 214), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
|
||||
painter->setPen(solidPen);
|
||||
painter->drawLine(cubicSegment.firstControlPoint().coordinate(),
|
||||
cubicSegment.secondControlPoint().coordinate());
|
||||
|
||||
QVector<double> dashVector;
|
||||
dashVector.append(4);
|
||||
dashVector.append(4);
|
||||
QPen dashedPen(QColor(104, 183, 214), 1, Qt::CustomDashLine, Qt::FlatCap, Qt::MiterJoin);
|
||||
dashedPen.setDashPattern(dashVector);
|
||||
painter->setPen(dashedPen);
|
||||
painter->drawLine(cubicSegment.secondControlPoint().coordinate(),
|
||||
cubicSegment.thirdControlPoint().coordinate());
|
||||
|
||||
painter->setPen(QPen(QColor(104, 183, 214), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
|
||||
painter->drawLine(cubicSegment.thirdControlPoint().coordinate(),
|
||||
cubicSegment.fourthControlPoint().coordinate());
|
||||
}
|
||||
|
||||
static void drawControlLines(const QList<CubicSegment> &cubicSegments, QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, cubicSegments)
|
||||
drawControlLine(cubicSegment, painter);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
static QRectF controlPointShape(-2, -2, 5, 5);
|
||||
|
||||
static void drawControlPoint(const ControlPoint &controlPoint, const QList<ControlPoint> &selectionPoints, QPainter *painter)
|
||||
{
|
||||
static QColor editPointColor(0, 110, 255);
|
||||
static QColor controlVertexColor(0, 110, 255);
|
||||
static QColor selectionPointColor(0, 255, 0);
|
||||
|
||||
double originX = controlPoint.coordinate().x();
|
||||
double originY = controlPoint.coordinate().y();
|
||||
|
||||
if (controlPoint.isEditPoint()) {
|
||||
if (selectionPoints.contains(controlPoint)) {
|
||||
painter->setBrush(selectionPointColor);
|
||||
painter->setPen(selectionPointColor);
|
||||
} else {
|
||||
painter->setBrush(editPointColor);
|
||||
painter->setPen(editPointColor);
|
||||
}
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
painter->drawRect(controlPointShape.adjusted(originX -1, originY - 1, originX - 1, originY - 1));
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
} else {
|
||||
if (selectionPoints.contains(controlPoint)) {
|
||||
painter->setBrush(selectionPointColor);
|
||||
painter->setPen(selectionPointColor);
|
||||
} else {
|
||||
painter->setBrush(controlVertexColor);
|
||||
painter->setPen(controlVertexColor);
|
||||
}
|
||||
painter->drawEllipse(controlPointShape.adjusted(originX, originY, originX, originY));
|
||||
}
|
||||
}
|
||||
|
||||
static void drawControlPoints(const QList<ControlPoint> &controlPoints, const QList<ControlPoint> &selectionPoints, QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
|
||||
foreach (const ControlPoint &controlPoint, controlPoints)
|
||||
drawControlPoint(controlPoint, selectionPoints, painter);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
static void drawPositionOverlay(const ControlPoint &controlPoint, QPainter *painter)
|
||||
{
|
||||
QPoint position = controlPoint.coordinate().toPoint();
|
||||
position.rx() += 3;
|
||||
position.ry() -= 3;
|
||||
|
||||
QString postionText(QString(QLatin1String("x: %1 y: %2")).arg(controlPoint.coordinate().x()).arg(controlPoint.coordinate().y()));
|
||||
painter->drawText(position, postionText);
|
||||
}
|
||||
|
||||
static void drawPostionOverlays(const QList<SelectionPoint> &selectedPoints, QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
QFont font = painter->font();
|
||||
font.setPixelSize(9);
|
||||
painter->setFont(font);
|
||||
painter->setPen(QColor(0, 0, 0));
|
||||
|
||||
foreach (const SelectionPoint &selectedPoint, selectedPoints)
|
||||
drawPositionOverlay(selectedPoint.controlPoint, painter);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
static void drawMultiSelectionRectangle(const QRectF &selectionRectangle, QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
static QColor selectionBrush(painter->pen().color());
|
||||
selectionBrush.setAlpha(50);
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
painter->setBrush(selectionBrush);
|
||||
painter->drawRect(selectionRectangle);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
painter->save();
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
if (!m_cubicSegments.isEmpty()) {
|
||||
drawCubicSegments(m_cubicSegments, painter);
|
||||
drawControlLines(m_cubicSegments, painter);
|
||||
drawControlPoints(controlPoints(), m_selectionManipulator.allControlPoints(), painter);
|
||||
drawPostionOverlays(m_selectionManipulator.singleSelectedPoints(), painter);
|
||||
if (m_selectionManipulator.isMultiSelecting())
|
||||
drawMultiSelectionRectangle(m_selectionManipulator.multiSelectionRectangle(), painter);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
FormEditorItem *PathItem::formEditorItem() const
|
||||
{
|
||||
return m_formEditorItem;
|
||||
}
|
||||
|
||||
static CubicSegment createCubicSegmentForLine(const ModelNode &lineNode, const ControlPoint &startControlPoint)
|
||||
{
|
||||
CubicSegment cubicSegment = CubicSegment::create();
|
||||
cubicSegment.setModelNode(lineNode);
|
||||
|
||||
if (lineNode.hasProperty("x")
|
||||
&& lineNode.hasProperty("y")) {
|
||||
|
||||
QPointF controlPoint0Line = startControlPoint.coordinate();
|
||||
QPointF controlPoint1Line(lineNode.variantProperty("x").value().toDouble(),
|
||||
lineNode.variantProperty("y").value().toDouble());
|
||||
|
||||
QPointF controlPoint1Cubic = controlPoint0Line + (1./3.) * (controlPoint1Line - controlPoint0Line);
|
||||
QPointF controlPoint2Cubic = controlPoint0Line + (2./3.) * (controlPoint1Line - controlPoint0Line);
|
||||
|
||||
cubicSegment.setFirstControlPoint(startControlPoint);
|
||||
cubicSegment.setSecondControlPoint(controlPoint1Cubic);
|
||||
cubicSegment.setThirdControlPoint(controlPoint2Cubic);
|
||||
cubicSegment.setFourthControlPoint(controlPoint1Line);
|
||||
} else {
|
||||
qWarning() << "PathLine has not all entries!";
|
||||
}
|
||||
|
||||
return cubicSegment;
|
||||
}
|
||||
|
||||
static CubicSegment createCubicSegmentForQuad(const ModelNode &quadNode, const ControlPoint &startControlPoint)
|
||||
{
|
||||
CubicSegment cubicSegment = CubicSegment::create();
|
||||
cubicSegment.setModelNode(quadNode);
|
||||
|
||||
if (quadNode.hasProperty("controlX")
|
||||
&& quadNode.hasProperty("controlY")
|
||||
&& quadNode.hasProperty("x")
|
||||
&& quadNode.hasProperty("y")) {
|
||||
QPointF controlPoint0Quad = startControlPoint.coordinate();
|
||||
QPointF controlPoint1Quad(quadNode.variantProperty("controlX").value().toDouble(),
|
||||
quadNode.variantProperty("controlY").value().toDouble());
|
||||
QPointF controlPoint2Quad(quadNode.variantProperty("x").value().toDouble(),
|
||||
quadNode.variantProperty("y").value().toDouble());
|
||||
|
||||
QPointF controlPoint1Cubic = controlPoint0Quad + (2./3.) * (controlPoint1Quad - controlPoint0Quad);
|
||||
QPointF controlPoint2Cubic = controlPoint2Quad + (2./3.) * (controlPoint1Quad - controlPoint2Quad);
|
||||
|
||||
cubicSegment.setFirstControlPoint(startControlPoint);
|
||||
cubicSegment.setSecondControlPoint(controlPoint1Cubic);
|
||||
cubicSegment.setThirdControlPoint(controlPoint2Cubic);
|
||||
cubicSegment.setFourthControlPoint(controlPoint2Quad);
|
||||
} else {
|
||||
qWarning() << "PathQuad has not all entries!";
|
||||
}
|
||||
|
||||
return cubicSegment;
|
||||
}
|
||||
|
||||
static CubicSegment createCubicSegmentForCubic(const ModelNode &cubicNode, const ControlPoint &startControlPoint)
|
||||
{
|
||||
CubicSegment cubicSegment = CubicSegment::create();
|
||||
cubicSegment.setModelNode(cubicNode);
|
||||
|
||||
if (cubicNode.hasProperty("control1X")
|
||||
&& cubicNode.hasProperty("control1Y")
|
||||
&& cubicNode.hasProperty("control2X")
|
||||
&& cubicNode.hasProperty("control2Y")
|
||||
&& cubicNode.hasProperty("x")
|
||||
&& cubicNode.hasProperty("y")) {
|
||||
|
||||
cubicSegment.setFirstControlPoint(startControlPoint);
|
||||
cubicSegment.setSecondControlPoint(cubicNode.variantProperty("control1X").value().toDouble(),
|
||||
cubicNode.variantProperty("control1Y").value().toDouble());
|
||||
cubicSegment.setThirdControlPoint(cubicNode.variantProperty("control2X").value().toDouble(),
|
||||
cubicNode.variantProperty("control2Y").value().toDouble());
|
||||
cubicSegment.setFourthControlPoint(cubicNode.variantProperty("x").value().toDouble(),
|
||||
cubicNode.variantProperty("y").value().toDouble());
|
||||
} else {
|
||||
qWarning() << "PathCubic has not all entries!";
|
||||
}
|
||||
|
||||
return cubicSegment;
|
||||
}
|
||||
|
||||
static QRectF boundingRectForPath(const QList<ControlPoint> &controlPoints)
|
||||
{
|
||||
double xMinimum = 0.;
|
||||
double xMaximum = 0.;
|
||||
double yMinimum = 0.;
|
||||
double yMaximum = 0.;
|
||||
|
||||
foreach (const ControlPoint & controlPoint, controlPoints) {
|
||||
xMinimum = qMin(xMinimum, controlPoint.coordinate().x());
|
||||
xMaximum = qMax(xMaximum, controlPoint.coordinate().x());
|
||||
yMinimum = qMin(yMinimum, controlPoint.coordinate().y());
|
||||
yMaximum = qMax(yMaximum, controlPoint.coordinate().y());
|
||||
}
|
||||
|
||||
return QRect(xMinimum, yMinimum, xMaximum - xMinimum, yMaximum - yMinimum);
|
||||
}
|
||||
|
||||
void PathItem::updateBoundingRect()
|
||||
{
|
||||
QRectF controlBoundingRect = boundingRectForPath(controlPoints()).adjusted(-100, -100, 200, 100);
|
||||
|
||||
if (m_selectionManipulator.isMultiSelecting())
|
||||
controlBoundingRect = controlBoundingRect.united(m_selectionManipulator.multiSelectionRectangle());
|
||||
|
||||
setBoundingRect(instanceBoundingRect().united(controlBoundingRect));
|
||||
}
|
||||
|
||||
QRectF PathItem::instanceBoundingRect() const
|
||||
{
|
||||
if (formEditorItem())
|
||||
return formEditorItem()->qmlItemNode().instanceBoundingRect();
|
||||
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
void PathItem::readControlPoints()
|
||||
{
|
||||
ModelNode pathNode = pathModelNode(formEditorItem());
|
||||
|
||||
m_cubicSegments.clear();
|
||||
|
||||
if (pathNode.hasNodeListProperty("pathElements")) {
|
||||
ControlPoint firstControlPoint(startPoint(pathNode));
|
||||
firstControlPoint.setPathModelNode(pathNode);
|
||||
firstControlPoint.setPointType(StartPoint);
|
||||
|
||||
QMap<QString, QVariant> actualAttributes;
|
||||
double percent = -1.0;
|
||||
|
||||
foreach (const ModelNode &childNode, pathNode.nodeListProperty("pathElements").toModelNodeList()) {
|
||||
|
||||
if (childNode.type() == "QtQuick.PathAttribute") {
|
||||
actualAttributes.insert(childNode.variantProperty("name").value().toString(), childNode.variantProperty("value").value());
|
||||
} else if (childNode.type() == "QtQuick.PathPercent") {
|
||||
percent = childNode.variantProperty("value").value().toDouble();
|
||||
} else {
|
||||
CubicSegment newCubicSegement;
|
||||
|
||||
if (childNode.type() == "QtQuick.PathLine")
|
||||
newCubicSegement = createCubicSegmentForLine(childNode, firstControlPoint);
|
||||
else if (childNode.type() == "QtQuick.PathQuad")
|
||||
newCubicSegement = createCubicSegmentForQuad(childNode, firstControlPoint);
|
||||
else if (childNode.type() == "QtQuick.PathCubic")
|
||||
newCubicSegement = createCubicSegmentForCubic(childNode, firstControlPoint);
|
||||
else
|
||||
continue;
|
||||
|
||||
newCubicSegement.setPercent(percent);
|
||||
newCubicSegement.setAttributes(actualAttributes);
|
||||
|
||||
firstControlPoint = newCubicSegement.fourthControlPoint();
|
||||
qDebug() << "Can be converted to quad" << newCubicSegement.canBeConvertedToQuad();
|
||||
qDebug() << "Can be converted to line" << newCubicSegement.canBeConvertedToLine();
|
||||
m_cubicSegments.append(newCubicSegement);
|
||||
actualAttributes.clear();
|
||||
percent = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
m_lastAttributes = actualAttributes;
|
||||
m_lastPercent = percent;
|
||||
|
||||
if (m_cubicSegments.first().firstControlPoint().coordinate() == m_cubicSegments.last().fourthControlPoint().coordinate()) {
|
||||
CubicSegment lastCubicSegment = m_cubicSegments.last();
|
||||
lastCubicSegment.setFourthControlPoint(m_cubicSegments.first().firstControlPoint());
|
||||
lastCubicSegment.fourthControlPoint().setPathModelNode(pathNode);
|
||||
lastCubicSegment.fourthControlPoint().setPointType(StartAndEndPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CubicSegment getMinimumDistanceSegment(const QPointF &pickPoint, const QList<CubicSegment> &cubicSegments, double maximumDistance, double *t = 0)
|
||||
{
|
||||
CubicSegment minimumDistanceSegment;
|
||||
double actualMinimumDistance = maximumDistance;
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, cubicSegments) {
|
||||
double tSegment = 0.;
|
||||
double cubicSegmentMinimumDistance = cubicSegment.minimumDistance(pickPoint, tSegment);
|
||||
if (cubicSegmentMinimumDistance < actualMinimumDistance) {
|
||||
minimumDistanceSegment = cubicSegment;
|
||||
actualMinimumDistance = cubicSegmentMinimumDistance;
|
||||
if (t)
|
||||
*t = tSegment;
|
||||
}
|
||||
}
|
||||
|
||||
return minimumDistanceSegment;
|
||||
}
|
||||
|
||||
void PathItem::splitCubicSegment(CubicSegment &cubicSegment, double t)
|
||||
{
|
||||
QPair<CubicSegment, CubicSegment> newCubicSegmentPair = cubicSegment.split(t);
|
||||
int indexOfOldCubicSegment = m_cubicSegments.indexOf(cubicSegment);
|
||||
|
||||
m_cubicSegments.removeAt(indexOfOldCubicSegment);
|
||||
m_cubicSegments.insert(indexOfOldCubicSegment, newCubicSegmentPair.first);
|
||||
m_cubicSegments.insert(indexOfOldCubicSegment + 1, newCubicSegmentPair.second);
|
||||
}
|
||||
|
||||
void PathItem::closePath()
|
||||
{
|
||||
if (!m_cubicSegments.isEmpty()) {
|
||||
CubicSegment firstCubicSegment = m_cubicSegments.first();
|
||||
CubicSegment lastCubicSegment = m_cubicSegments.last();
|
||||
lastCubicSegment.setFourthControlPoint(firstCubicSegment.firstControlPoint());
|
||||
writePathAsCubicSegmentsOnly();
|
||||
}
|
||||
}
|
||||
|
||||
void PathItem::openPath()
|
||||
{
|
||||
if (!m_cubicSegments.isEmpty()) {
|
||||
CubicSegment firstCubicSegment = m_cubicSegments.first();
|
||||
CubicSegment lastCubicSegment = m_cubicSegments.last();
|
||||
QPointF newEndPoint = firstCubicSegment.firstControlPoint().coordinate();
|
||||
newEndPoint.setX(newEndPoint.x() + 10.);
|
||||
lastCubicSegment.setFourthControlPoint(ControlPoint(newEndPoint));
|
||||
writePathAsCubicSegmentsOnly();
|
||||
}
|
||||
}
|
||||
|
||||
QAction *PathItem::createClosedPathAction(QMenu *contextMenu) const
|
||||
{
|
||||
QAction *closedPathAction = new QAction(contextMenu);
|
||||
closedPathAction->setCheckable(true);
|
||||
closedPathAction->setChecked(isClosedPath());
|
||||
closedPathAction->setText(tr("Closed Path"));
|
||||
contextMenu->addAction(closedPathAction);
|
||||
|
||||
if (m_cubicSegments.count() == 1)
|
||||
closedPathAction->setDisabled(true);
|
||||
|
||||
return closedPathAction;
|
||||
}
|
||||
|
||||
void PathItem::createGlobalContextMenu(const QPoint &menuPosition)
|
||||
{
|
||||
QMenu contextMenu;
|
||||
|
||||
QAction *closedPathAction = createClosedPathAction(&contextMenu);
|
||||
|
||||
QAction *activatedAction = contextMenu.exec(menuPosition);
|
||||
|
||||
if (activatedAction == closedPathAction)
|
||||
makePathClosed(closedPathAction->isChecked());
|
||||
}
|
||||
|
||||
void PathItem::createCubicSegmentContextMenu(CubicSegment &cubicSegment, const QPoint &menuPosition, double t)
|
||||
{
|
||||
QMenu contextMenu;
|
||||
|
||||
QAction *splitSegmentAction = new QAction(&contextMenu);
|
||||
splitSegmentAction->setText(tr("Split Segment"));
|
||||
contextMenu.addAction(splitSegmentAction);
|
||||
|
||||
QAction *straightLinePointAction = new QAction(&contextMenu);
|
||||
straightLinePointAction->setText(tr("Make Curve Segment Straight"));
|
||||
contextMenu.addAction(straightLinePointAction);
|
||||
|
||||
if (m_cubicSegments.count() == 1 && isClosedPath())
|
||||
straightLinePointAction->setDisabled(true);
|
||||
|
||||
QAction *closedPathAction = createClosedPathAction(&contextMenu);
|
||||
|
||||
QAction *activatedAction = contextMenu.exec(menuPosition);
|
||||
|
||||
if (activatedAction == straightLinePointAction) {
|
||||
cubicSegment.makeStraightLine();
|
||||
PathUpdateDisabler pathItemDisabler(this, PathUpdateDisabler::DontUpdatePath);
|
||||
RewriterTransaction rewriterTransaction =
|
||||
cubicSegment.modelNode().view()->beginRewriterTransaction(QByteArrayLiteral("PathItem::createCubicSegmentContextMenu"));
|
||||
cubicSegment.updateModelNode();
|
||||
rewriterTransaction.commit();
|
||||
} else if (activatedAction == splitSegmentAction) {
|
||||
splitCubicSegment(cubicSegment, t);
|
||||
writePathAsCubicSegmentsOnly();
|
||||
} else if (activatedAction == closedPathAction) {
|
||||
makePathClosed(closedPathAction->isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PathItem::createEditPointContextMenu(const ControlPoint &controlPoint, const QPoint &menuPosition)
|
||||
{
|
||||
QMenu contextMenu;
|
||||
QAction *removeEditPointAction = new QAction(&contextMenu);
|
||||
removeEditPointAction->setText(tr("Remove Edit Point"));
|
||||
contextMenu.addAction(removeEditPointAction);
|
||||
|
||||
QAction *closedPathAction = createClosedPathAction(&contextMenu);
|
||||
|
||||
if (m_cubicSegments.count() <= 1 || (m_cubicSegments.count() == 2 && isClosedPath()))
|
||||
removeEditPointAction->setDisabled(true);
|
||||
|
||||
QAction *activatedAction = contextMenu.exec(menuPosition);
|
||||
|
||||
if (activatedAction == removeEditPointAction)
|
||||
removeEditPoint(controlPoint);
|
||||
else if (activatedAction == closedPathAction)
|
||||
makePathClosed(closedPathAction->isChecked());
|
||||
}
|
||||
|
||||
const QList<ControlPoint> PathItem::controlPoints() const
|
||||
{
|
||||
QList<ControlPoint> controlPointList;
|
||||
controlPointList.reserve((m_cubicSegments.count() * 4));
|
||||
|
||||
if (!m_cubicSegments.isEmpty())
|
||||
controlPointList.append(m_cubicSegments.first().firstControlPoint());
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, m_cubicSegments) {
|
||||
controlPointList.append(cubicSegment.secondControlPoint());
|
||||
controlPointList.append(cubicSegment.thirdControlPoint());
|
||||
controlPointList.append(cubicSegment.fourthControlPoint());
|
||||
}
|
||||
|
||||
if (isClosedPath())
|
||||
controlPointList.pop_back();
|
||||
|
||||
return controlPointList;
|
||||
}
|
||||
|
||||
bool hasLineOrQuadPathElements(const QList<ModelNode> &modelNodes)
|
||||
{
|
||||
foreach (const ModelNode &modelNode, modelNodes) {
|
||||
if (modelNode.type() == "QtQuick.PathLine"
|
||||
|| modelNode.type() == "QtQuick.PathQuad")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PathItem::updatePath()
|
||||
{
|
||||
if (m_dontUpdatePath)
|
||||
return;
|
||||
|
||||
if (hasPath(formEditorItem())) {
|
||||
readControlPoints();
|
||||
|
||||
ModelNode pathNode = pathModelNode(formEditorItem());
|
||||
|
||||
if (hasLineOrQuadPathElements(pathNode.nodeListProperty("pathElements").toModelNodeList()))
|
||||
writePathAsCubicSegmentsOnly();
|
||||
}
|
||||
|
||||
updateBoundingRect();
|
||||
update();
|
||||
}
|
||||
|
||||
QRectF PathItem::boundingRect() const
|
||||
{
|
||||
return m_boundingRect;
|
||||
}
|
||||
|
||||
void PathItem::setBoundingRect(const QRectF &boundingRect)
|
||||
{
|
||||
m_boundingRect = boundingRect;
|
||||
}
|
||||
|
||||
static bool controlPointIsNearMousePosition(const ControlPoint &controlPoint, const QPointF &mousePosition)
|
||||
{
|
||||
QPointF distanceVector = controlPoint.coordinate() - mousePosition;
|
||||
|
||||
if (distanceVector.manhattanLength() < 10)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool controlPointsAreNearMousePosition(const QList<ControlPoint> &controlPoints, const QPointF &mousePosition)
|
||||
{
|
||||
foreach (const ControlPoint &controlPoint, controlPoints) {
|
||||
if (controlPointIsNearMousePosition(controlPoint, mousePosition))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ControlPoint pickControlPoint(const QList<ControlPoint> &controlPoints, const QPointF &mousePosition)
|
||||
{
|
||||
foreach (const ControlPoint &controlPoint, controlPoints) {
|
||||
if (controlPointIsNearMousePosition(controlPoint, mousePosition))
|
||||
return controlPoint;
|
||||
}
|
||||
|
||||
return ControlPoint();
|
||||
}
|
||||
|
||||
void PathItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (m_selectionManipulator.hasMultiSelection()) {
|
||||
m_selectionManipulator.setStartPoint(event->pos());
|
||||
} else {
|
||||
ControlPoint pickedControlPoint = pickControlPoint(controlPoints(), event->pos());
|
||||
|
||||
if (pickedControlPoint.isValid()) {
|
||||
m_selectionManipulator.addSingleControlPointSmartly(pickedControlPoint);
|
||||
m_selectionManipulator.startMoving(event->pos());
|
||||
} else {
|
||||
m_selectionManipulator.startMultiSelection(event->pos());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMoveStartDistance(const QPointF &startPoint, const QPointF &updatePoint)
|
||||
{
|
||||
return (startPoint - updatePoint).manhattanLength() > 10;
|
||||
}
|
||||
|
||||
void PathItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (controlPointsAreNearMousePosition(controlPoints(), event->pos()))
|
||||
setCursor(Qt::SizeAllCursor);
|
||||
else
|
||||
setCursor(Qt::ArrowCursor);
|
||||
|
||||
PathUpdateDisabler pathUpdateDisabler(this, PathUpdateDisabler::DontUpdatePath);
|
||||
if (event->buttons().testFlag(Qt::LeftButton)) {
|
||||
if (m_selectionManipulator.isMultiSelecting()) {
|
||||
m_selectionManipulator.updateMultiSelection(event->pos());
|
||||
update();
|
||||
} else if (m_selectionManipulator.hasSingleSelection()) {
|
||||
setCursor(Qt::SizeAllCursor);
|
||||
m_selectionManipulator.updateMoving(event->pos(), event->modifiers());
|
||||
updatePathModelNodes(m_selectionManipulator.allSelectionSinglePoints());
|
||||
updateBoundingRect();
|
||||
update();
|
||||
} else if (m_selectionManipulator.hasMultiSelection()) {
|
||||
setCursor(Qt::SizeAllCursor);
|
||||
if (m_selectionManipulator.isMoving()) {
|
||||
m_selectionManipulator.updateMoving(event->pos(), event->modifiers());
|
||||
updatePathModelNodes(m_selectionManipulator.allSelectionSinglePoints());
|
||||
updateBoundingRect();
|
||||
update();
|
||||
} else if (hasMoveStartDistance(m_selectionManipulator.startPoint(), event->pos())) {
|
||||
m_selectionManipulator.startMoving(m_selectionManipulator.startPoint());
|
||||
m_selectionManipulator.updateMoving(event->pos(), event->modifiers());
|
||||
updatePathModelNodes(m_selectionManipulator.allSelectionSinglePoints());
|
||||
updateBoundingRect();
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathItem::updatePathModelNodes(const QList<SelectionPoint> &changedPoints)
|
||||
{
|
||||
PathUpdateDisabler pathUpdateDisabler(this, PathUpdateDisabler::DontUpdatePath);
|
||||
|
||||
RewriterTransaction rewriterTransaction =
|
||||
formEditorItem()->qmlItemNode().view()->beginRewriterTransaction(QByteArrayLiteral("PathItem::createCubicSegmentContextMenu"));
|
||||
|
||||
foreach (SelectionPoint changedPoint, changedPoints)
|
||||
changedPoint.controlPoint.updateModelNode();
|
||||
|
||||
rewriterTransaction.commit();
|
||||
}
|
||||
|
||||
void PathItem::disablePathUpdates()
|
||||
{
|
||||
m_dontUpdatePath = true;
|
||||
}
|
||||
|
||||
void PathItem::enablePathUpdates()
|
||||
{
|
||||
m_dontUpdatePath = false;
|
||||
}
|
||||
|
||||
bool PathItem::pathUpdatesDisabled() const
|
||||
{
|
||||
return m_dontUpdatePath;
|
||||
}
|
||||
|
||||
void PathItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (m_selectionManipulator.isMultiSelecting()) {
|
||||
m_selectionManipulator.updateMultiSelection(event->pos());
|
||||
m_selectionManipulator.endMultiSelection();
|
||||
} else if (m_selectionManipulator.hasSingleSelection()) {
|
||||
m_selectionManipulator.updateMoving(event->pos(), event->modifiers());
|
||||
updatePathModelNodes(m_selectionManipulator.allSelectionSinglePoints());
|
||||
updateBoundingRect();
|
||||
m_selectionManipulator.clearSingleSelection();
|
||||
} else if (m_selectionManipulator.hasMultiSelection()) {
|
||||
if (m_selectionManipulator.isMoving()) {
|
||||
m_selectionManipulator.updateMoving(event->pos(), event->modifiers());
|
||||
m_selectionManipulator.endMoving();
|
||||
updatePathModelNodes(m_selectionManipulator.multiSelectedPoints());
|
||||
updateBoundingRect();
|
||||
} else {
|
||||
m_selectionManipulator.clearMultiSelection();
|
||||
}
|
||||
}
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
ControlPoint pickedControlPoint = pickControlPoint(controlPoints(), event->pos());
|
||||
if (pickedControlPoint.isEditPoint()) {
|
||||
createEditPointContextMenu(pickedControlPoint, event->screenPos());
|
||||
} else {
|
||||
double t;
|
||||
CubicSegment minimumDistanceSegment = getMinimumDistanceSegment(event->pos(), m_cubicSegments, 20., &t);
|
||||
if (minimumDistanceSegment.isValid())
|
||||
createCubicSegmentContextMenu(minimumDistanceSegment, event->screenPos(), t);
|
||||
else
|
||||
createGlobalContextMenu(event->screenPos());
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
bool PathItem::isClosedPath() const
|
||||
{
|
||||
if (m_cubicSegments.isEmpty())
|
||||
return false;
|
||||
|
||||
ControlPoint firstControlPoint = m_cubicSegments.first().firstControlPoint();
|
||||
ControlPoint lastControlPoint = m_cubicSegments.last().fourthControlPoint();
|
||||
|
||||
return firstControlPoint == lastControlPoint;
|
||||
}
|
||||
|
||||
void PathItem::makePathClosed(bool pathShoudlBeClosed)
|
||||
{
|
||||
if (pathShoudlBeClosed && !isClosedPath())
|
||||
closePath();
|
||||
else if (!pathShoudlBeClosed && isClosedPath())
|
||||
openPath();
|
||||
}
|
||||
|
||||
QList<CubicSegment> cubicSegmentsContainingControlPoint(const ControlPoint &controlPoint, const QList<CubicSegment> &allCubicSegments)
|
||||
{
|
||||
QList<CubicSegment> cubicSegmentsHasControlPoint;
|
||||
|
||||
foreach (const CubicSegment &cubicSegment, allCubicSegments) {
|
||||
if (cubicSegment.controlPoints().contains(controlPoint))
|
||||
cubicSegmentsHasControlPoint.append(cubicSegment);
|
||||
}
|
||||
|
||||
return cubicSegmentsHasControlPoint;
|
||||
}
|
||||
|
||||
void PathItem::removeEditPoint(const ControlPoint &controlPoint)
|
||||
{
|
||||
QList<CubicSegment> cubicSegments = cubicSegmentsContainingControlPoint(controlPoint, m_cubicSegments);
|
||||
|
||||
if (cubicSegments.count() == 1) {
|
||||
m_cubicSegments.removeOne(cubicSegments.first());
|
||||
} else if (cubicSegments.count() == 2){
|
||||
CubicSegment mergedCubicSegment = CubicSegment::create();
|
||||
CubicSegment firstCubicSegment = cubicSegments.at(0);
|
||||
CubicSegment secondCubicSegment = cubicSegments.at(1);
|
||||
mergedCubicSegment.setFirstControlPoint(firstCubicSegment.firstControlPoint());
|
||||
mergedCubicSegment.setSecondControlPoint(firstCubicSegment.secondControlPoint());
|
||||
mergedCubicSegment.setThirdControlPoint(secondCubicSegment.thirdControlPoint());
|
||||
mergedCubicSegment.setFourthControlPoint(secondCubicSegment.fourthControlPoint());
|
||||
|
||||
int indexOfFirstCubicSegment = m_cubicSegments.indexOf(firstCubicSegment);
|
||||
m_cubicSegments.removeAt(indexOfFirstCubicSegment);
|
||||
m_cubicSegments.removeAt(indexOfFirstCubicSegment);
|
||||
m_cubicSegments.insert(indexOfFirstCubicSegment, mergedCubicSegment);
|
||||
}
|
||||
|
||||
writePathAsCubicSegmentsOnly();
|
||||
}
|
||||
|
||||
PathUpdateDisabler::PathUpdateDisabler(PathItem *pathItem, PathUpdate updatePath)
|
||||
: m_pathItem(pathItem),
|
||||
m_updatePath(updatePath)
|
||||
{
|
||||
pathItem->disablePathUpdates();
|
||||
}
|
||||
|
||||
PathUpdateDisabler::~PathUpdateDisabler()
|
||||
{
|
||||
m_pathItem->enablePathUpdates();
|
||||
if (m_updatePath == UpdatePath)
|
||||
m_pathItem->updatePath();
|
||||
}
|
||||
|
||||
}
|
143
src/plugins/qmldesigner/qmldesignerextension/pathtool/pathitem.h
Normal file
143
src/plugins/qmldesigner/qmldesignerextension/pathtool/pathitem.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef PATHITEM_H
|
||||
#define PATHITEM_H
|
||||
|
||||
#include <QGraphicsObject>
|
||||
#include <QWeakPointer>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <qmldesignercorelib_global.h>
|
||||
|
||||
#include "cubicsegment.h"
|
||||
#include "pathselectionmanipulator.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextEdit;
|
||||
class QAction;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class FormEditorScene;
|
||||
class FormEditorItem;
|
||||
class PathItem;
|
||||
|
||||
class PathUpdateDisabler
|
||||
{
|
||||
public:
|
||||
enum PathUpdate
|
||||
{
|
||||
UpdatePath,
|
||||
DontUpdatePath
|
||||
};
|
||||
|
||||
PathUpdateDisabler(PathItem *pathItem, PathUpdate updatePath = UpdatePath);
|
||||
~PathUpdateDisabler();
|
||||
|
||||
private:
|
||||
PathItem *m_pathItem;
|
||||
PathUpdate m_updatePath;
|
||||
};
|
||||
|
||||
class PathItem : public QGraphicsObject
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class PathUpdateDisabler;
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Type = 0xEAAC
|
||||
};
|
||||
PathItem(FormEditorScene* scene);
|
||||
~PathItem();
|
||||
int type() const override;
|
||||
|
||||
void setFormEditorItem(FormEditorItem *formEditorItem);
|
||||
FormEditorItem *formEditorItem() const;
|
||||
|
||||
QList<QGraphicsItem*> findAllChildItems() const;
|
||||
|
||||
void updatePath();
|
||||
void writePathToProperty();
|
||||
void writePathAsCubicSegmentsOnly();
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
|
||||
QRectF boundingRect() const override;
|
||||
void setBoundingRect(const QRectF &boundingRect);
|
||||
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
bool isClosedPath() const;
|
||||
const QList<ControlPoint> controlPoints() const;
|
||||
|
||||
protected:
|
||||
void updateBoundingRect();
|
||||
QRectF instanceBoundingRect() const;
|
||||
void writeLinePath(ModelNode pathNode, const CubicSegment &cubicSegment);
|
||||
void writeQuadPath(ModelNode pathNode, const CubicSegment &cubicSegment);
|
||||
void writeCubicPath(ModelNode pathNode, const CubicSegment &cubicSegment);
|
||||
void writePathAttributes(ModelNode pathNode, const QMap<QString, QVariant> &attributes);
|
||||
void writePathPercent(ModelNode pathNode, double percent);
|
||||
void readControlPoints();
|
||||
void splitCubicSegment(CubicSegment &cubicSegment, double t);
|
||||
void closePath();
|
||||
void openPath();
|
||||
void createGlobalContextMenu(const QPoint &menuPosition);
|
||||
void createCubicSegmentContextMenu(CubicSegment &cubicSegment, const QPoint &menuPosition, double t);
|
||||
void createEditPointContextMenu(const ControlPoint &controlPoint, const QPoint &menuPosition);
|
||||
void makePathClosed(bool pathShoudlBeClosed);
|
||||
void removeEditPoint(const ControlPoint &controlPoint);
|
||||
void updatePathModelNodes(const QList<SelectionPoint> &changedPoints);
|
||||
void disablePathUpdates();
|
||||
void enablePathUpdates();
|
||||
bool pathUpdatesDisabled() const;
|
||||
QAction *createClosedPathAction(QMenu *contextMenu) const;
|
||||
|
||||
signals:
|
||||
void textChanged(const QString &text);
|
||||
|
||||
private:
|
||||
PathSelectionManipulator m_selectionManipulator;
|
||||
QList<CubicSegment> m_cubicSegments;
|
||||
QPointF m_startPoint;
|
||||
QRectF m_boundingRect;
|
||||
QMap<QString, QVariant> m_lastAttributes;
|
||||
double m_lastPercent;
|
||||
FormEditorItem *m_formEditorItem;
|
||||
bool m_dontUpdatePath;
|
||||
};
|
||||
|
||||
inline int PathItem::type() const
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PATHITEM_H
|
@@ -0,0 +1,301 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pathselectionmanipulator.h"
|
||||
|
||||
#include "pathitem.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
PathSelectionManipulator::PathSelectionManipulator(PathItem *pathItem)
|
||||
: m_pathItem(pathItem),
|
||||
m_isMultiSelecting(false),
|
||||
m_isMoving(false)
|
||||
{
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::clear()
|
||||
{
|
||||
clearSingleSelection();
|
||||
clearMultiSelection();
|
||||
m_isMultiSelecting = false;
|
||||
m_isMoving = false;
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::clearSingleSelection()
|
||||
{
|
||||
m_singleSelectedPoints.clear();
|
||||
m_automaticallyAddedSinglePoints.clear();
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::clearMultiSelection()
|
||||
{
|
||||
m_multiSelectedPoints.clear();
|
||||
}
|
||||
|
||||
static SelectionPoint createSelectionPoint(const ControlPoint &controlPoint)
|
||||
{
|
||||
SelectionPoint selectionPoint;
|
||||
selectionPoint.controlPoint = controlPoint;
|
||||
selectionPoint.startPosition = controlPoint.coordinate();
|
||||
|
||||
return selectionPoint;
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::addMultiSelectionControlPoint(const ControlPoint &controlPoint)
|
||||
{
|
||||
m_multiSelectedPoints.append(createSelectionPoint(controlPoint));
|
||||
}
|
||||
|
||||
static ControlPoint getControlPoint(const QList<ControlPoint> &selectedPoints, const ControlPoint &controlPoint, int indexOffset, bool isClosedPath)
|
||||
{
|
||||
int controlPointIndex = selectedPoints.indexOf(controlPoint);
|
||||
if (controlPointIndex >= 0) {
|
||||
int offsetIndex = controlPointIndex + indexOffset;
|
||||
if (offsetIndex >= 0 && offsetIndex < selectedPoints.count())
|
||||
return selectedPoints.at(offsetIndex);
|
||||
else if (isClosedPath) {
|
||||
if (offsetIndex == -1)
|
||||
return selectedPoints.last();
|
||||
else if (offsetIndex < selectedPoints.count())
|
||||
return selectedPoints.at(1);
|
||||
}
|
||||
}
|
||||
|
||||
return ControlPoint();
|
||||
}
|
||||
|
||||
QList<SelectionPoint> PathSelectionManipulator::singleSelectedPoints()
|
||||
{
|
||||
return m_singleSelectedPoints;
|
||||
}
|
||||
|
||||
QList<SelectionPoint> PathSelectionManipulator::automaticallyAddedSinglePoints()
|
||||
{
|
||||
return m_automaticallyAddedSinglePoints;
|
||||
}
|
||||
|
||||
QList<SelectionPoint> PathSelectionManipulator::allSelectionSinglePoints()
|
||||
{
|
||||
|
||||
return m_singleSelectedPoints + m_automaticallyAddedSinglePoints;
|
||||
}
|
||||
|
||||
QList<SelectionPoint> PathSelectionManipulator::multiSelectedPoints()
|
||||
{
|
||||
return m_multiSelectedPoints;
|
||||
}
|
||||
|
||||
QList<SelectionPoint> PathSelectionManipulator::allSelectionPoints()
|
||||
{
|
||||
return m_singleSelectedPoints + m_multiSelectedPoints + m_automaticallyAddedSinglePoints;
|
||||
}
|
||||
|
||||
QList<ControlPoint> PathSelectionManipulator::allControlPoints()
|
||||
{
|
||||
QList<ControlPoint> controlPoints;
|
||||
|
||||
foreach (const SelectionPoint &selectionPoint, m_singleSelectedPoints)
|
||||
controlPoints.append(selectionPoint.controlPoint);
|
||||
|
||||
foreach (const SelectionPoint &selectionPoint, m_automaticallyAddedSinglePoints)
|
||||
controlPoints.append(selectionPoint.controlPoint);
|
||||
|
||||
foreach (const SelectionPoint &selectionPoint, m_multiSelectedPoints)
|
||||
controlPoints.append(selectionPoint.controlPoint);
|
||||
|
||||
return controlPoints;
|
||||
}
|
||||
|
||||
bool PathSelectionManipulator::hasSingleSelection() const
|
||||
{
|
||||
return !m_singleSelectedPoints.isEmpty();
|
||||
}
|
||||
|
||||
bool PathSelectionManipulator::hasMultiSelection() const
|
||||
{
|
||||
return !m_multiSelectedPoints.isEmpty();
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::startMultiSelection(const QPointF &startPoint)
|
||||
{
|
||||
m_startPoint = startPoint;
|
||||
m_isMultiSelecting = true;
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::updateMultiSelection(const QPointF &updatePoint)
|
||||
{
|
||||
clearMultiSelection();
|
||||
|
||||
m_updatePoint = updatePoint;
|
||||
|
||||
QRectF selectionRect(m_startPoint, updatePoint);
|
||||
|
||||
foreach (const ControlPoint &controlPoint, m_pathItem->controlPoints()) {
|
||||
if (selectionRect.contains(controlPoint.coordinate()))
|
||||
addMultiSelectionControlPoint(controlPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::endMultiSelection()
|
||||
{
|
||||
m_isMultiSelecting = false;
|
||||
}
|
||||
|
||||
SelectionPoint::SelectionPoint()
|
||||
{
|
||||
}
|
||||
|
||||
SelectionPoint::SelectionPoint(const ControlPoint &controlPoint)
|
||||
: controlPoint(controlPoint)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator ==(const SelectionPoint &firstSelectionPoint, const SelectionPoint &secondSelectionPoint)
|
||||
{
|
||||
return firstSelectionPoint.controlPoint == secondSelectionPoint.controlPoint;
|
||||
}
|
||||
|
||||
QPointF PathSelectionManipulator::multiSelectionStartPoint() const
|
||||
{
|
||||
return m_startPoint;
|
||||
}
|
||||
|
||||
bool PathSelectionManipulator::isMultiSelecting() const
|
||||
{
|
||||
return m_isMultiSelecting;
|
||||
}
|
||||
|
||||
QRectF PathSelectionManipulator::multiSelectionRectangle() const
|
||||
{
|
||||
return QRectF(m_startPoint, m_updatePoint);
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::setStartPoint(const QPointF &startPoint)
|
||||
{
|
||||
m_startPoint = startPoint;
|
||||
}
|
||||
|
||||
QPointF PathSelectionManipulator::startPoint() const
|
||||
{
|
||||
return m_startPoint;
|
||||
}
|
||||
|
||||
double snapFactor(Qt::KeyboardModifiers keyboardModifier)
|
||||
{
|
||||
if (keyboardModifier.testFlag(Qt::ControlModifier))
|
||||
return 10.;
|
||||
|
||||
return 1.;
|
||||
}
|
||||
|
||||
QPointF roundedVector(const QPointF &vector, double factor = 1.)
|
||||
{
|
||||
QPointF roundedPosition;
|
||||
|
||||
roundedPosition.setX(qRound(vector.x() / factor) * factor);
|
||||
roundedPosition.setY(qRound(vector.y() / factor) * factor);
|
||||
|
||||
return roundedPosition;
|
||||
}
|
||||
|
||||
QPointF manipulatedVector(const QPointF &vector, Qt::KeyboardModifiers keyboardModifier)
|
||||
{
|
||||
QPointF manipulatedVector = roundedVector(vector, snapFactor(keyboardModifier));
|
||||
|
||||
if (keyboardModifier.testFlag(Qt::ShiftModifier))
|
||||
manipulatedVector.setX(0.);
|
||||
|
||||
if (keyboardModifier.testFlag(Qt::AltModifier))
|
||||
manipulatedVector.setY(0.);
|
||||
|
||||
return manipulatedVector;
|
||||
}
|
||||
|
||||
static void moveControlPoints(const QList<SelectionPoint> &movePoints, const QPointF &offsetVector)
|
||||
{
|
||||
foreach (SelectionPoint movePoint, movePoints)
|
||||
movePoint.controlPoint.setCoordinate(movePoint.startPosition + offsetVector);
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::startMoving(const QPointF &startPoint)
|
||||
{
|
||||
m_isMoving = true;
|
||||
m_startPoint = startPoint;
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::updateMoving(const QPointF &updatePoint, Qt::KeyboardModifiers keyboardModifier)
|
||||
{
|
||||
m_updatePoint = updatePoint;
|
||||
QPointF offsetVector = manipulatedVector(updatePoint - m_startPoint, keyboardModifier) ;
|
||||
moveControlPoints(allSelectionPoints(), offsetVector);
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::endMoving()
|
||||
{
|
||||
updateMultiSelectedStartPoint();
|
||||
m_isMoving = false;
|
||||
}
|
||||
|
||||
bool PathSelectionManipulator::isMoving() const
|
||||
{
|
||||
return m_isMoving;
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::updateMultiSelectedStartPoint()
|
||||
{
|
||||
QList<SelectionPoint> oldSelectionPoints = m_multiSelectedPoints;
|
||||
|
||||
m_multiSelectedPoints.clear();
|
||||
|
||||
foreach (SelectionPoint selectionPoint, oldSelectionPoints) {
|
||||
selectionPoint.startPosition = selectionPoint.controlPoint.coordinate();
|
||||
m_multiSelectedPoints.append(selectionPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::addSingleControlPoint(const ControlPoint &controlPoint)
|
||||
{
|
||||
m_singleSelectedPoints.append(createSelectionPoint(controlPoint));
|
||||
}
|
||||
|
||||
void PathSelectionManipulator::addSingleControlPointSmartly(const ControlPoint &editPoint)
|
||||
{
|
||||
m_singleSelectedPoints.append(createSelectionPoint(editPoint));
|
||||
|
||||
if (editPoint.isEditPoint()) {
|
||||
ControlPoint previousControlPoint = getControlPoint(m_pathItem->controlPoints(), editPoint, -1, m_pathItem->isClosedPath());
|
||||
if (previousControlPoint.isValid())
|
||||
m_automaticallyAddedSinglePoints.append(createSelectionPoint(previousControlPoint));
|
||||
|
||||
ControlPoint nextControlPoint= getControlPoint(m_pathItem->controlPoints(), editPoint, 1, m_pathItem->isClosedPath());
|
||||
if (nextControlPoint.isValid())
|
||||
m_automaticallyAddedSinglePoints.append(createSelectionPoint(nextControlPoint));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QMlDesigner
|
@@ -0,0 +1,102 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef QMLDESIGNER_PATHSELECTIONMANIPULATOR_H
|
||||
#define QMLDESIGNER_PATHSELECTIONMANIPULATOR_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QPoint>
|
||||
|
||||
#include "controlpoint.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class PathItem;
|
||||
|
||||
struct SelectionPoint
|
||||
{
|
||||
SelectionPoint();
|
||||
SelectionPoint(const ControlPoint &controlPoint);
|
||||
ControlPoint controlPoint;
|
||||
QPointF startPosition;
|
||||
};
|
||||
|
||||
class PathSelectionManipulator
|
||||
{
|
||||
public:
|
||||
PathSelectionManipulator(PathItem *pathItem);
|
||||
|
||||
void clear();
|
||||
void clearSingleSelection();
|
||||
void clearMultiSelection();
|
||||
|
||||
void addMultiSelectionControlPoint(const ControlPoint &controlPoint);
|
||||
void addSingleControlPoint(const ControlPoint &controlPoint);
|
||||
void addSingleControlPointSmartly(const ControlPoint &editPoint);
|
||||
|
||||
QList<SelectionPoint> singleSelectedPoints();
|
||||
QList<SelectionPoint> automaticallyAddedSinglePoints();
|
||||
QList<SelectionPoint> allSelectionSinglePoints();
|
||||
QList<SelectionPoint> multiSelectedPoints();
|
||||
QList<SelectionPoint> allSelectionPoints();
|
||||
|
||||
QList<ControlPoint> allControlPoints();
|
||||
|
||||
bool hasSingleSelection() const;
|
||||
bool hasMultiSelection() const;
|
||||
|
||||
void startMultiSelection(const QPointF &startPoint);
|
||||
void updateMultiSelection(const QPointF &updatePoint);
|
||||
void endMultiSelection();
|
||||
QPointF multiSelectionStartPoint() const;
|
||||
bool isMultiSelecting() const;
|
||||
|
||||
QRectF multiSelectionRectangle() const;
|
||||
|
||||
void setStartPoint(const QPointF &startPoint);
|
||||
QPointF startPoint() const;
|
||||
void startMoving(const QPointF &startPoint);
|
||||
void updateMoving(const QPointF &updatePoint, Qt::KeyboardModifiers keyboardModifier);
|
||||
void endMoving();
|
||||
bool isMoving() const;
|
||||
|
||||
void updateMultiSelectedStartPoint();
|
||||
|
||||
private:
|
||||
QList<SelectionPoint> m_singleSelectedPoints;
|
||||
QList<SelectionPoint> m_automaticallyAddedSinglePoints;
|
||||
QList<SelectionPoint> m_multiSelectedPoints;
|
||||
QPointF m_startPoint;
|
||||
QPointF m_updatePoint;
|
||||
PathItem *m_pathItem;
|
||||
bool m_isMultiSelecting;
|
||||
bool m_isMoving;
|
||||
};
|
||||
|
||||
bool operator ==(const SelectionPoint& firstSelectionPoint, const SelectionPoint& secondSelectionPoint);
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // QMLDESIGNER_PATHSELECTIONMANIPULATOR_H
|
@@ -0,0 +1,323 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pathtool.h"
|
||||
|
||||
#include <formeditorscene.h>
|
||||
#include <formeditorview.h>
|
||||
#include <formeditorwidget.h>
|
||||
#include <itemutilfunctions.h>
|
||||
#include <formeditoritem.h>
|
||||
|
||||
#include "pathitem.h"
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
#include <qmlitemnode.h>
|
||||
#include <nodeproperty.h>
|
||||
#include <nodelistproperty.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <abstractaction.h>
|
||||
#include <designeractionmanager.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
static bool isNonSupportedPathElement(const ModelNode &pathElement)
|
||||
{
|
||||
if (pathElement.type() == "QtQuick.PathCubic")
|
||||
return false;
|
||||
|
||||
if (pathElement.type() == "QtQuick.PathAttribute")
|
||||
return false;
|
||||
|
||||
if (pathElement.type() == "QtQuick.PathPercent")
|
||||
return false;
|
||||
|
||||
if (pathElement.type() == "QtQuick.PathAttribute")
|
||||
return false;
|
||||
|
||||
if (pathElement.type() == "QtQuick.PathQuad")
|
||||
return false;
|
||||
|
||||
if (pathElement.type() == "QtQuick.PathLine")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int pathRankForModelNode(const ModelNode &modelNode) {
|
||||
if (modelNode.metaInfo().hasProperty("path")) {
|
||||
if (modelNode.hasNodeProperty("path")) {
|
||||
ModelNode pathNode = modelNode.nodeProperty("path").modelNode();
|
||||
if (pathNode.metaInfo().isSubclassOf("QtQuick.Path", -1, -1) && pathNode.hasNodeListProperty("pathElements")) {
|
||||
QList<ModelNode> pathElements = pathNode.nodeListProperty("pathElements").toModelNodeList();
|
||||
if (pathElements.isEmpty())
|
||||
return 0;
|
||||
|
||||
foreach (const ModelNode &pathElement, pathElements) {
|
||||
if (isNonSupportedPathElement(pathElement))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 20;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class PathToolAction : public AbstractAction
|
||||
{
|
||||
public:
|
||||
PathToolAction() : AbstractAction(QCoreApplication::translate("PathToolAction","Edit Path")) {}
|
||||
|
||||
QByteArray category() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray menuId() const
|
||||
{
|
||||
return "PathTool";
|
||||
}
|
||||
|
||||
int priority() const
|
||||
{
|
||||
return CustomActionsPriority;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return Action;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isVisible(const SelectionContext &selectionContext) const
|
||||
{
|
||||
if (selectionContext.scenePosition().isNull())
|
||||
return false;
|
||||
|
||||
if (selectionContext.singleNodeIsSelected())
|
||||
return pathRankForModelNode(selectionContext.currentSingleSelectedNode()) > 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEnabled(const SelectionContext &selectionContext) const
|
||||
{
|
||||
return isVisible(selectionContext);
|
||||
}
|
||||
};
|
||||
|
||||
PathTool::PathTool()
|
||||
: QObject(),
|
||||
AbstractCustomTool(),
|
||||
m_pathToolView(this)
|
||||
{
|
||||
PathToolAction *textToolAction = new PathToolAction;
|
||||
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(textToolAction);
|
||||
connect(textToolAction->action(),
|
||||
SIGNAL(triggered()),
|
||||
this,
|
||||
SLOT(changeToPathTool()));
|
||||
}
|
||||
|
||||
PathTool::~PathTool()
|
||||
{
|
||||
}
|
||||
|
||||
void PathTool::clear()
|
||||
{
|
||||
if (m_pathItem)
|
||||
m_pathItem->deleteLater();
|
||||
|
||||
AbstractFormEditorTool::clear();
|
||||
}
|
||||
|
||||
void PathTool::mousePressEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * event)
|
||||
{
|
||||
event->setPos(m_pathItem->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(m_pathItem->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(m_pathItem.data(), event);
|
||||
}
|
||||
|
||||
void PathTool::mouseMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
event->setPos(m_pathItem->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(m_pathItem->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(m_pathItem.data(), event);
|
||||
}
|
||||
|
||||
void PathTool::hoverMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * event)
|
||||
{
|
||||
event->setPos(m_pathItem->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(m_pathItem->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(m_pathItem.data(), event);
|
||||
}
|
||||
|
||||
void PathTool::keyPressEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
m_pathItem->writePathToProperty();
|
||||
keyEvent->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void PathTool::keyReleaseEvent(QKeyEvent * keyEvent)
|
||||
{
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
keyEvent->accept();
|
||||
if (m_pathToolView.model())
|
||||
m_pathToolView.model()->detachView(&m_pathToolView);
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PathTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PathTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PathTool::mouseReleaseEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
event->setPos(m_pathItem->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(m_pathItem->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(m_pathItem.data(), event);
|
||||
}
|
||||
|
||||
|
||||
void PathTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (m_pathItem.data() && !m_pathItem->boundingRect().contains(m_pathItem->mapFromScene(event->scenePos()))) {
|
||||
m_pathItem->writePathToProperty();
|
||||
view()->changeToSelectionTool();
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void PathTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList)
|
||||
{
|
||||
if (m_pathItem == 0)
|
||||
return;
|
||||
|
||||
if (removedItemList.contains(m_pathItem->formEditorItem()))
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
static bool hasPathProperty(FormEditorItem *formEditorItem)
|
||||
{
|
||||
return formEditorItem->qmlItemNode().modelNode().metaInfo().hasProperty("path");
|
||||
}
|
||||
|
||||
void PathTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
|
||||
{
|
||||
if (m_pathItem.data() && itemList.contains(m_pathItem->formEditorItem()))
|
||||
m_pathItem->writePathToProperty();
|
||||
|
||||
delete m_pathItem.data();
|
||||
if (!itemList.isEmpty() && hasPathProperty(itemList.first())) {
|
||||
FormEditorItem *formEditorItem = itemList.first();
|
||||
m_pathItem = new PathItem(scene());
|
||||
m_pathItem->setParentItem(scene()->manipulatorLayerItem());
|
||||
m_pathItem->setFormEditorItem(formEditorItem);
|
||||
formEditorItem->qmlItemNode().modelNode().model()->attachView(&m_pathToolView);
|
||||
} else {
|
||||
if (m_pathToolView.model())
|
||||
m_pathToolView.model()->detachView(&m_pathToolView);
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PathTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void PathTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void PathTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList)
|
||||
{
|
||||
typedef QPair<ModelNode, PropertyName> ModelNodePropertyNamePair;
|
||||
foreach (const ModelNodePropertyNamePair &propertyPair, propertyList) {
|
||||
if (propertyPair.first == m_pathItem->formEditorItem()->qmlItemNode().modelNode()
|
||||
&& propertyPair.second == "path")
|
||||
m_pathItem->updatePath();
|
||||
}
|
||||
}
|
||||
|
||||
void PathTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
int PathTool::wantHandleItem(const ModelNode &modelNode) const
|
||||
{
|
||||
return pathRankForModelNode(modelNode);
|
||||
}
|
||||
|
||||
QString PathTool::name() const
|
||||
{
|
||||
return QCoreApplication::translate("PathTool", "Path Tool");
|
||||
}
|
||||
|
||||
ModelNode PathTool::editingPathViewModelNode() const
|
||||
{
|
||||
if (m_pathItem)
|
||||
return m_pathItem->formEditorItem()->qmlItemNode().modelNode();
|
||||
|
||||
return ModelNode();
|
||||
}
|
||||
|
||||
void PathTool::pathChanged()
|
||||
{
|
||||
if (m_pathItem)
|
||||
m_pathItem->updatePath();
|
||||
}
|
||||
|
||||
void PathTool::changeToPathTool()
|
||||
{
|
||||
if (m_pathToolView.model())
|
||||
m_pathToolView.model()->detachView(&m_pathToolView);
|
||||
view()->changeToCustomTool(this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef PATHTOOL_H
|
||||
#define PATHTOOL_H
|
||||
|
||||
#include "abstractcustomtool.h"
|
||||
#include "selectionindicator.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QColorDialog>
|
||||
|
||||
#include "pathtoolview.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class PathItem;
|
||||
|
||||
class PathTool : public QObject, public AbstractCustomTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PathTool();
|
||||
~PathTool();
|
||||
|
||||
void mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void keyReleaseEvent(QKeyEvent *keyEvent) override;
|
||||
|
||||
void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
|
||||
void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
|
||||
void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
|
||||
void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
int wantHandleItem(const ModelNode &modelNode) const override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
ModelNode editingPathViewModelNode() const;
|
||||
|
||||
void pathChanged();
|
||||
|
||||
private slots:
|
||||
void changeToPathTool();
|
||||
|
||||
private:
|
||||
PathToolView m_pathToolView;
|
||||
QPointer<PathItem> m_pathItem;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // PATHTOOL_H
|
||||
|
@@ -0,0 +1,13 @@
|
||||
HEADERS += $$PWD/pathtool.h
|
||||
HEADERS += $$PWD/pathselectionmanipulator.h
|
||||
HEADERS += $$PWD/pathtoolview.h
|
||||
HEADERS += $$PWD/controlpoint.h
|
||||
HEADERS += $$PWD/cubicsegment.h
|
||||
HEADERS += $$PWD/pathitem.h
|
||||
|
||||
SOURCES += $$PWD/pathtool.cpp
|
||||
SOURCES += $$PWD/pathselectionmanipulator.cpp
|
||||
SOURCES += $$PWD/pathtoolview.cpp
|
||||
SOURCES += $$PWD/controlpoint.cpp
|
||||
SOURCES += $$PWD/cubicsegment.cpp
|
||||
SOURCES += $$PWD/pathitem.cpp
|
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pathtoolview.h"
|
||||
|
||||
#include <nodeproperty.h>
|
||||
#include <variantproperty.h>
|
||||
#include <modelnode.h>
|
||||
#include <metainfo.h>
|
||||
|
||||
#include "pathtool.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
PathToolView::PathToolView(PathTool *pathTool)
|
||||
: AbstractView(),
|
||||
m_pathTool(pathTool)
|
||||
{
|
||||
}
|
||||
|
||||
static bool isInEditedPath(const NodeAbstractProperty &propertyParent, const ModelNode &editingPathViewModelNode)
|
||||
{
|
||||
if (editingPathViewModelNode.isValid()) {
|
||||
if (editingPathViewModelNode.hasNodeProperty("path")) {
|
||||
ModelNode pathModelNode = editingPathViewModelNode.nodeProperty("path").modelNode();
|
||||
if (pathModelNode.metaInfo().isSubclassOf("QtQuick.Path", -1, -1)) {
|
||||
if (propertyParent.name() == "pathElements" && propertyParent.parentModelNode() == pathModelNode)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PathToolView::nodeReparented(const ModelNode & /*node*/,
|
||||
const NodeAbstractProperty & newPropertyParent,
|
||||
const NodeAbstractProperty & /*oldPropertyParent*/,
|
||||
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
if (isInEditedPath(newPropertyParent, m_pathTool->editingPathViewModelNode()))
|
||||
m_pathTool->pathChanged();
|
||||
}
|
||||
|
||||
bool variantPropertyInEditedPath(const VariantProperty &variantProperty, const ModelNode &editingPathViewModelNode)
|
||||
{
|
||||
ModelNode pathElementModelNode = variantProperty.parentModelNode();
|
||||
if (pathElementModelNode.hasParentProperty()) {
|
||||
if (isInEditedPath(pathElementModelNode.parentProperty(), editingPathViewModelNode))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changesEditedPath(const QList<VariantProperty> &propertyList, const ModelNode &editingPathViewModelNode)
|
||||
{
|
||||
foreach (const VariantProperty variantProperty, propertyList) {
|
||||
if (variantPropertyInEditedPath(variantProperty, editingPathViewModelNode))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PathToolView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||
{
|
||||
if (changesEditedPath(propertyList, m_pathTool->editingPathViewModelNode()))
|
||||
m_pathTool->pathChanged();
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLDESIGNER_PATHTOOLVIEW_H
|
||||
#define QMLDESIGNER_PATHTOOLVIEW_H
|
||||
|
||||
#include <abstractview.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class PathTool;
|
||||
|
||||
class PathToolView : public AbstractView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PathToolView(PathTool *pathTool);
|
||||
|
||||
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override;
|
||||
void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override;
|
||||
|
||||
private:
|
||||
PathTool *m_pathTool;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
#endif // QMLDESIGNER_PATHTOOLVIEW_H
|
@@ -0,0 +1,14 @@
|
||||
DEFINES += QMLDESIGNEREXTENSION_LIBRARY
|
||||
|
||||
INCLUDEPATH *= $$PWD
|
||||
|
||||
# QmlDesignerExtension files
|
||||
|
||||
include(sourcetool/sourcetool.pri)
|
||||
include(colortool/colortool.pri)
|
||||
include(texttool/texttool.pri)
|
||||
include(pathtool/pathtool.pri)
|
||||
|
||||
include(connectioneditor/connectioneditor.pri)
|
||||
|
||||
|
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLDESIGNEREXTENSION_GLOBAL_H
|
||||
#define QMLDESIGNEREXTENSION_GLOBAL_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(QMLDESIGNEREXTENSION_LIBRARY)
|
||||
# define QMLDESIGNEREXTENSIONSHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define QMLDESIGNEREXTENSIONSHARED_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#endif // QMLDESIGNEREXTENSION_GLOBAL_H
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMLDESIGNEREXTENSIONCONSTANTS_H
|
||||
#define QMLDESIGNEREXTENSIONCONSTANTS_H
|
||||
|
||||
namespace QmlDesignerExtension {
|
||||
namespace Constants {
|
||||
|
||||
const char ACTION_ID[] = "QmlDesignerExtension.Action";
|
||||
const char MENU_ID[] = "QmlDesignerExtension.Menu";
|
||||
|
||||
} // namespace QmlDesignerExtension
|
||||
} // namespace Constants
|
||||
|
||||
#endif // QMLDESIGNEREXTENSIONCONSTANTS_H
|
||||
|
@@ -0,0 +1,255 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "sourcetool.h"
|
||||
|
||||
#include "formeditorscene.h"
|
||||
#include "formeditorview.h"
|
||||
#include "formeditorwidget.h"
|
||||
#include "itemutilfunctions.h"
|
||||
#include "formeditoritem.h"
|
||||
|
||||
#include "resizehandleitem.h"
|
||||
|
||||
#include "nodemetainfo.h"
|
||||
#include "qmlitemnode.h"
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <abstractaction.h>
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
#include <QUrl>
|
||||
|
||||
namespace {
|
||||
|
||||
bool modelNodeHasUrlSource(const QmlDesigner::ModelNode &modelNode)
|
||||
{
|
||||
QmlDesigner::NodeMetaInfo metaInfo = modelNode.metaInfo();
|
||||
if (metaInfo.isValid()) {
|
||||
if (metaInfo.hasProperty("source")) {
|
||||
if (metaInfo.propertyTypeName("source") == "QUrl")
|
||||
return true;
|
||||
if (metaInfo.propertyTypeName("source") == "url")
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class SourceToolAction : public AbstractAction
|
||||
{
|
||||
public:
|
||||
SourceToolAction() : AbstractAction(QCoreApplication::translate("SourceToolAction","Change Source Url...")) {}
|
||||
|
||||
QByteArray category() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray menuId() const
|
||||
{
|
||||
return "SourceTool";
|
||||
}
|
||||
|
||||
int priority() const
|
||||
{
|
||||
return CustomActionsPriority;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return Action;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isVisible(const SelectionContext &selectionContext) const
|
||||
{
|
||||
if (selectionContext.singleNodeIsSelected())
|
||||
return modelNodeHasUrlSource(selectionContext.currentSingleSelectedNode());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEnabled(const SelectionContext &selectionContext) const
|
||||
{
|
||||
return isVisible(selectionContext);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SourceTool::SourceTool()
|
||||
: QObject(), AbstractCustomTool()
|
||||
{
|
||||
SourceToolAction *sourceToolAction = new SourceToolAction;
|
||||
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(sourceToolAction);
|
||||
connect(sourceToolAction->action(),
|
||||
SIGNAL(triggered()),
|
||||
this,
|
||||
SLOT(changeToSourceTool()));
|
||||
}
|
||||
|
||||
SourceTool::~SourceTool()
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::clear()
|
||||
{
|
||||
AbstractFormEditorTool::clear();
|
||||
}
|
||||
|
||||
void SourceTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mousePressEvent(itemList, event);
|
||||
}
|
||||
|
||||
void SourceTool::mouseMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::hoverMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::keyPressEvent(QKeyEvent * /*keyEvent*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mouseReleaseEvent(itemList, event);
|
||||
}
|
||||
|
||||
|
||||
void SourceTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event);
|
||||
}
|
||||
|
||||
void SourceTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList)
|
||||
{
|
||||
if (removedItemList.contains(m_formEditorItem.data()))
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
static QString baseDirectory(const QUrl &url)
|
||||
{
|
||||
QString filePath = url.toLocalFile();
|
||||
return QFileInfo(filePath).absoluteDir().path();
|
||||
}
|
||||
|
||||
void SourceTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
|
||||
{
|
||||
if (!itemList.isEmpty()) {
|
||||
m_formEditorItem = itemList.first();
|
||||
m_oldFileName = m_formEditorItem.data()->qmlItemNode().modelValue("source").toString();
|
||||
|
||||
QString openDirectory = baseDirectory(view()->model()->fileUrl());
|
||||
if (openDirectory.isEmpty())
|
||||
openDirectory = baseDirectory(view()->model()->fileUrl());
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName(0,
|
||||
tr("Open File"),
|
||||
openDirectory);
|
||||
fileSelected(fileName);
|
||||
|
||||
} else {
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
}
|
||||
|
||||
void SourceTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SourceTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
int SourceTool::wantHandleItem(const ModelNode &modelNode) const
|
||||
{
|
||||
if (modelNodeHasUrlSource(modelNode))
|
||||
return 15;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString SourceTool::name() const
|
||||
{
|
||||
return tr("Source Tool");
|
||||
}
|
||||
|
||||
void SourceTool::fileSelected(const QString &fileName)
|
||||
{
|
||||
if (m_formEditorItem
|
||||
&& QFileInfo(fileName).isFile()) {
|
||||
QString modelFilePath = view()->model()->fileUrl().toLocalFile();
|
||||
QDir modelFileDirectory = QFileInfo(modelFilePath).absoluteDir();
|
||||
QString relativeFilePath = modelFileDirectory.relativeFilePath(fileName);
|
||||
if (m_oldFileName != relativeFilePath) {
|
||||
m_formEditorItem.data()->qmlItemNode().setVariantProperty("source", relativeFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
void SourceTool::changeToSourceTool()
|
||||
{
|
||||
view()->changeToCustomTool(this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SOURCETOOL_H
|
||||
#define SOURCETOOL_H
|
||||
|
||||
#include <abstractcustomtool.h>
|
||||
#include "selectionindicator.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QFileDialog>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class SelectionContext;
|
||||
|
||||
class SourceTool : public QObject, public AbstractCustomTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourceTool();
|
||||
~SourceTool();
|
||||
|
||||
void mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void keyReleaseEvent(QKeyEvent *keyEvent) override;
|
||||
|
||||
void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
|
||||
void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
|
||||
void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
|
||||
void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
int wantHandleItem(const ModelNode &modelNode) const override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
private slots:
|
||||
void changeToSourceTool();
|
||||
|
||||
private:
|
||||
/* private methods */
|
||||
void fileSelected(const QString &fileName);
|
||||
|
||||
|
||||
/* members */
|
||||
QPointer<FormEditorItem> m_formEditorItem;
|
||||
QString m_oldFileName;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // SOURCETOOL_H
|
@@ -0,0 +1,3 @@
|
||||
HEADERS += $$PWD/sourcetool.h
|
||||
|
||||
SOURCES += $$PWD/sourcetool.cpp
|
@@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "textedititem.h"
|
||||
|
||||
#include <formeditorscene.h>
|
||||
#include <QTextEdit>
|
||||
#include <QLineEdit>
|
||||
#include <nodemetainfo.h>
|
||||
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
TextEditItem::TextEditItem(FormEditorScene* scene)
|
||||
: QGraphicsProxyWidget(),
|
||||
m_lineEdit(new QLineEdit),
|
||||
m_textEdit(new QTextEdit),
|
||||
m_formEditorItem(0)
|
||||
{
|
||||
scene->addItem(this);
|
||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||
|
||||
setWidget(m_lineEdit.data());
|
||||
m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
|
||||
m_lineEdit->setFocus();
|
||||
connect(m_lineEdit.data(), &QLineEdit::returnPressed, this, &TextEditItem::returnPressed);
|
||||
}
|
||||
|
||||
TextEditItem::~TextEditItem()
|
||||
{
|
||||
setWidget(0);
|
||||
m_formEditorItem = 0;
|
||||
}
|
||||
|
||||
void TextEditItem::writeTextToProperty()
|
||||
{
|
||||
if (m_formEditorItem) {
|
||||
if (text().isEmpty())
|
||||
m_formEditorItem->qmlItemNode().removeProperty("text");
|
||||
else if (m_formEditorItem->qmlItemNode().isTranslatableText("text"))
|
||||
m_formEditorItem->qmlItemNode().setBindingProperty("text", QmlObjectNode::generateTranslatableText(text()));
|
||||
else
|
||||
m_formEditorItem->qmlItemNode().setVariantProperty("text", text());
|
||||
}
|
||||
}
|
||||
|
||||
QString TextEditItem::text() const
|
||||
{
|
||||
if (widget() == m_lineEdit.data())
|
||||
return m_lineEdit->text();
|
||||
else if (widget() == m_textEdit.data())
|
||||
return m_textEdit->toPlainText();
|
||||
return QString();
|
||||
}
|
||||
|
||||
void TextEditItem::setFormEditorItem(FormEditorItem *formEditorItem)
|
||||
{
|
||||
m_formEditorItem = formEditorItem;
|
||||
QRectF rect = formEditorItem->qmlItemNode().instancePaintedBoundingRect().united(formEditorItem->qmlItemNode().instanceBoundingRect()).adjusted(-12, -4, 12 ,4);
|
||||
setGeometry(rect);
|
||||
m_textEdit->setMaximumSize(rect.size().toSize());
|
||||
|
||||
NodeMetaInfo metaInfo = m_formEditorItem->qmlItemNode().modelNode().metaInfo();
|
||||
if (metaInfo.isValid() &&
|
||||
(metaInfo.isSubclassOf("QtQuick.TextEdit", -1, -1)
|
||||
|| metaInfo.isSubclassOf("QtQuick.Controls.TextArea", -1, -1))) {
|
||||
setWidget(m_textEdit.data());
|
||||
m_textEdit->setFocus();
|
||||
}
|
||||
|
||||
setTransform(formEditorItem->sceneTransform());
|
||||
updateText();
|
||||
}
|
||||
|
||||
FormEditorItem *TextEditItem::formEditorItem() const
|
||||
{
|
||||
return m_formEditorItem;
|
||||
}
|
||||
|
||||
void TextEditItem::updateText()
|
||||
{
|
||||
if (formEditorItem()) {
|
||||
if (widget() == m_lineEdit.data()) {
|
||||
m_lineEdit->setText(formEditorItem()->qmlItemNode().stripedTranslatableText("text"));
|
||||
m_lineEdit->selectAll();
|
||||
} else if (widget() == m_textEdit.data()) {
|
||||
m_textEdit->setText(formEditorItem()->qmlItemNode().stripedTranslatableText("text"));
|
||||
m_textEdit->selectAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TEXTEDITITEM_H
|
||||
#define TEXTEDITITEM_H
|
||||
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QWeakPointer>
|
||||
#include <QScopedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextEdit;
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class FormEditorScene;
|
||||
class FormEditorItem;
|
||||
|
||||
class TextEditItem : public QGraphicsProxyWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Type = 0xEAAB
|
||||
};
|
||||
TextEditItem(FormEditorScene* scene);
|
||||
~TextEditItem();
|
||||
int type() const;
|
||||
|
||||
void setFormEditorItem(FormEditorItem *formEditorItem);
|
||||
FormEditorItem *formEditorItem() const;
|
||||
|
||||
QList<QGraphicsItem*> findAllChildItems() const;
|
||||
|
||||
void updateText();
|
||||
void writeTextToProperty();
|
||||
|
||||
signals:
|
||||
void textChanged(const QString &text);
|
||||
void returnPressed();
|
||||
|
||||
private:
|
||||
QString text() const;
|
||||
|
||||
QScopedPointer<QLineEdit> m_lineEdit;
|
||||
QScopedPointer<QTextEdit> m_textEdit;
|
||||
FormEditorItem *m_formEditorItem;
|
||||
};
|
||||
|
||||
inline int TextEditItem::type() const
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TEXTEDITITEM_H
|
@@ -0,0 +1,269 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "texttool.h"
|
||||
|
||||
#include "formeditorscene.h"
|
||||
#include "formeditorview.h"
|
||||
#include "formeditorwidget.h"
|
||||
#include "itemutilfunctions.h"
|
||||
#include "formeditoritem.h"
|
||||
|
||||
#include "resizehandleitem.h"
|
||||
#include "textedititem.h"
|
||||
|
||||
#include "nodemetainfo.h"
|
||||
#include "qmlitemnode.h"
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <abstractaction.h>
|
||||
#include <designeractionmanager.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class TextToolAction : public AbstractAction
|
||||
{
|
||||
public:
|
||||
TextToolAction() : AbstractAction(QCoreApplication::translate("TextToolAction","Edit Text")) {}
|
||||
|
||||
QByteArray category() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray menuId() const
|
||||
{
|
||||
return "TextTool";
|
||||
}
|
||||
|
||||
int priority() const
|
||||
{
|
||||
return CustomActionsPriority;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return Action;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isVisible(const SelectionContext &selectionContext) const
|
||||
{
|
||||
if (selectionContext.scenePosition().isNull())
|
||||
return false;
|
||||
|
||||
if (selectionContext.singleNodeIsSelected())
|
||||
return selectionContext.currentSingleSelectedNode().metaInfo().hasProperty("text");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEnabled(const SelectionContext &selectionContext) const
|
||||
{
|
||||
return isVisible(selectionContext);
|
||||
}
|
||||
};
|
||||
|
||||
TextTool::TextTool()
|
||||
: QObject() , AbstractCustomTool()
|
||||
{
|
||||
TextToolAction *textToolAction = new TextToolAction;
|
||||
QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(textToolAction);
|
||||
connect(textToolAction->action(),
|
||||
SIGNAL(triggered()),
|
||||
this,
|
||||
SLOT(changeToTextTool()));
|
||||
}
|
||||
|
||||
TextTool::~TextTool()
|
||||
{
|
||||
}
|
||||
|
||||
void TextTool::clear()
|
||||
{
|
||||
if (textItem())
|
||||
textItem()->clearFocus();
|
||||
textItem()->deleteLater();
|
||||
|
||||
AbstractFormEditorTool::clear();
|
||||
}
|
||||
|
||||
void TextTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
event->setPos(textItem()->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(textItem()->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(textItem(), event);
|
||||
AbstractFormEditorTool::mousePressEvent(itemList, event);
|
||||
}
|
||||
|
||||
void TextTool::mouseMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{ event->setPos(textItem()->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(textItem()->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(textItem(), event);
|
||||
}
|
||||
|
||||
void TextTool::hoverMoveEvent(const QList<QGraphicsItem*> & /*itemList*/,
|
||||
QGraphicsSceneMouseEvent * event)
|
||||
{
|
||||
event->setPos(textItem()->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(textItem()->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(textItem(), event);
|
||||
}
|
||||
|
||||
void TextTool::keyPressEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
textItem()->writeTextToProperty();
|
||||
keyEvent->accept();
|
||||
} else {
|
||||
scene()->sendEvent(textItem(), keyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void TextTool::keyReleaseEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
keyEvent->accept();
|
||||
view()->changeToSelectionTool();
|
||||
} else {
|
||||
scene()->sendEvent(textItem(), keyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void TextTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (!itemList.contains(textItem())) {
|
||||
textItem()->writeTextToProperty();
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
AbstractFormEditorTool::mouseReleaseEvent(itemList, event);
|
||||
}
|
||||
|
||||
|
||||
void TextTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> & /*itemList*/, QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (textItem() && !textItem()->boundingRect().contains(textItem()->mapFromScene(event->scenePos()))) {
|
||||
textItem()->writeTextToProperty();
|
||||
view()->changeToSelectionTool();
|
||||
} else {
|
||||
event->setPos(textItem()->mapFromScene(event->scenePos()));
|
||||
event->setLastPos(textItem()->mapFromScene(event->lastScenePos()));
|
||||
scene()->sendEvent(textItem(), event);
|
||||
}
|
||||
}
|
||||
|
||||
void TextTool::itemsAboutToRemoved(const QList<FormEditorItem*> &removedItemList)
|
||||
{
|
||||
if (textItem() == 0)
|
||||
return;
|
||||
|
||||
if (removedItemList.contains(textItem()->formEditorItem()))
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
|
||||
void TextTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
|
||||
{
|
||||
if (textItem()) {
|
||||
textItem()->writeTextToProperty();
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
if (!itemList.isEmpty()) {
|
||||
FormEditorItem *formEditorItem = itemList.first();
|
||||
m_textItem = new TextEditItem(scene());
|
||||
textItem()->setParentItem(scene()->manipulatorLayerItem());
|
||||
textItem()->setFormEditorItem(formEditorItem);
|
||||
connect(textItem(), &TextEditItem::returnPressed, [this] {
|
||||
textItem()->writeTextToProperty();
|
||||
view()->changeToSelectionTool();
|
||||
});
|
||||
} else {
|
||||
view()->changeToSelectionTool();
|
||||
}
|
||||
}
|
||||
|
||||
void TextTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void TextTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
void TextTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList)
|
||||
{
|
||||
typedef QPair<ModelNode, PropertyName> ModelNodePropertyNamePair;
|
||||
foreach (const ModelNodePropertyNamePair &propertyPair, propertyList) {
|
||||
if (propertyPair.first == textItem()->formEditorItem()->qmlItemNode().modelNode()
|
||||
&& propertyPair.second == "text")
|
||||
textItem()->updateText();
|
||||
}
|
||||
}
|
||||
|
||||
void TextTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
|
||||
{
|
||||
}
|
||||
|
||||
int TextTool::wantHandleItem(const ModelNode &modelNode) const
|
||||
{
|
||||
if (modelNode.metaInfo().hasProperty("text"))
|
||||
return 20;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString TextTool::name() const
|
||||
{
|
||||
return QCoreApplication::translate("TextTool", "Text Tool");
|
||||
}
|
||||
|
||||
void TextTool::changeToTextTool()
|
||||
{
|
||||
view()->changeToCustomTool(this);
|
||||
}
|
||||
|
||||
TextEditItem *TextTool::textItem() const
|
||||
{
|
||||
return m_textItem.data();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TEXTTOOL_H
|
||||
#define TEXTTOOL_H
|
||||
|
||||
#include "abstractcustomtool.h"
|
||||
#include "selectionindicator.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QColorDialog>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class TextEditItem;
|
||||
|
||||
class TextTool : public QObject, public AbstractCustomTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextTool();
|
||||
~TextTool();
|
||||
|
||||
void mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void keyReleaseEvent(QKeyEvent *keyEvent) override;
|
||||
|
||||
void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
QGraphicsSceneDragDropEvent * event) override;
|
||||
|
||||
void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
|
||||
void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
|
||||
void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
|
||||
|
||||
void clear() override;
|
||||
|
||||
void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
|
||||
|
||||
int wantHandleItem(const ModelNode &modelNode) const override;
|
||||
|
||||
QString name() const override;
|
||||
|
||||
private slots:
|
||||
void changeToTextTool();
|
||||
|
||||
protected:
|
||||
TextEditItem *textItem() const;
|
||||
|
||||
private:
|
||||
QPointer<TextEditItem> m_textItem;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // TEXTTOOL_H
|
||||
|
@@ -0,0 +1,5 @@
|
||||
HEADERS += $$PWD/texttool.h
|
||||
HEADERS += $$PWD/textedititem.h
|
||||
|
||||
SOURCES += $$PWD/texttool.cpp
|
||||
SOURCES += $$PWD/textedititem.cpp
|
@@ -20,6 +20,7 @@ include(components/stateseditor/stateseditor.pri)
|
||||
include(components/resources/resources.pri)
|
||||
include(components/debugview/debugview.pri)
|
||||
include(components/importmanager/importmanager.pri)
|
||||
include(qmldesignerextension/qmldesignerextension.pri)
|
||||
include(qmldesignerplugin.pri)
|
||||
|
||||
DEFINES -= QT_NO_CAST_FROM_ASCII
|
||||
|
Reference in New Issue
Block a user