summaryrefslogtreecommitdiff
path: root/src/plugins/qmldesigner/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmldesigner/components')
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h12
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp99
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp33
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h8
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp133
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp9
-rw-r--r--src/plugins/qmldesigner/components/componentcore/selectioncontext.h13
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.cpp46
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h86
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp29
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h1
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp11
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditor.pri6
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp629
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.h41
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp170
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizemanipulator.h3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizetool.cpp3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/transitiontool.cpp438
-rw-r--r--src/plugins/qmldesigner/components/formeditor/transitiontool.h98
-rw-r--r--src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp8
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp29
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp37
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp12
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h1
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h1
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp5
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.h1
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.cpp33
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.h4
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp11
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp45
36 files changed, 1797 insertions, 268 deletions
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index 2965bcd90b..e76b5c1ff7 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -39,8 +39,10 @@ const char qmlPreviewCategory[] = "QmlPreview";
const char editCategory[] = "Edit";
const char anchorsCategory[] = "Anchors";
const char positionCategory[] = "Position";
+const char groupCategory[] = "Group";
const char layoutCategory[] = "Layout";
const char flowCategory[] = "Flow";
+const char flowEffectCategory[] = "FlowEffect";
const char flowConnectionCategory[] = "FlowConnection";
const char stackedContainerCategory[] = "StackedContainer";
const char genericToolBarCategory[] = "GenericToolBar";
@@ -57,6 +59,8 @@ const char anchorsFillCommandId[] = "AnchorsFill";
const char anchorsResetCommandId[] = "AnchorsReset";
const char removePositionerCommandId[] = "RemovePositioner";
const char createFlowActionAreaCommandId[] = "CreateFlowActionArea";
+const char setFlowStartCommandId[] = "SetFlowStart";
+const char selectFlowEffectCommandId[] = "SelectFlowEffect";
const char layoutRowPositionerCommandId[] = "LayoutRowPositioner";
const char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner";
const char layoutGridPositionerCommandId[] = "LayoutGridPositioner";
@@ -76,15 +80,19 @@ const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedContainer";
const char flowAssignEffectCommandId[] = "AssignFlowEffect";
+const char addToGroupItemCommandId[] = "AddToGroupItem";
const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection");
const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect");
+const char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select Effect");
const char stackCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stack (z)");
const char editCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit");
const char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Anchors");
const char positionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position");
+const char groupCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group");
const char layoutCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout");
const char flowCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow");
+const char flowEffectCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow Effects");
const char stackedContainerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stacked Container");
const char cutSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Cut");
@@ -124,8 +132,11 @@ const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerCon
const char layoutFlowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position in Flow");
const char removePositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Positioner");
const char createFlowActionAreaDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Flow Action");
+const char setFlowStartDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Flow Start");
const char removeLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Layout");
+const char addToGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group in GroupItem");
+
const char addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Item");
const char addTabBarToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Tab Bar");
const char increaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Increase Index");
@@ -165,6 +176,7 @@ const int priorityStackCategory = 180;
const int priorityEditCategory = 160;
const int priorityAnchorsCategory = 140;
const int priorityFlowCategory = 240;
+const int priorityGroupCategory = 140;
const int priorityPositionCategory = 130;
const int priorityLayoutCategory = 120;
const int priorityStackedContainerCategory = priorityLayoutCategory;
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 42f95952db..07246f175c 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -346,12 +346,28 @@ bool isFlowItem(const SelectionContext &context)
&& QmlFlowItemNode::isValidQmlFlowItemNode(context.currentSingleSelectedNode());
}
+bool isFlowTarget(const SelectionContext &context)
+{
+ return context.singleNodeIsSelected()
+ && QmlFlowTargetNode::isFlowEditorTarget(context.currentSingleSelectedNode());
+}
+
bool isFlowTransitionItem(const SelectionContext &context)
{
return context.singleNodeIsSelected()
&& QmlFlowItemNode::isFlowTransition(context.currentSingleSelectedNode());
}
+bool isFlowTransitionItemWithEffect(const SelectionContext &context)
+{
+ if (!isFlowTransitionItem(context))
+ return false;
+
+ ModelNode node = context.currentSingleSelectedNode();
+
+ return node.hasNodeProperty("effect");
+}
+
bool isFlowActionItemItem(const SelectionContext &context)
{
const ModelNode selectedNode = context.currentSingleSelectedNode();
@@ -362,9 +378,9 @@ bool isFlowActionItemItem(const SelectionContext &context)
|| QmlVisualNode::isFlowWildcard(selectedNode));
}
-bool isFlowItemOrTransition(const SelectionContext &context)
+bool isFlowTargetOrTransition(const SelectionContext &context)
{
- return isFlowItem(context) || isFlowTransitionItem(context);
+ return isFlowTarget(context) || isFlowTransitionItem(context);
}
class FlowActionConnectAction : public ActionGroup
@@ -642,6 +658,12 @@ bool positionOptionVisible(const SelectionContext &context)
|| isPositioner(context);
}
+bool studioComponentsAvailable(const SelectionContext &context)
+{
+ const Import import = Import::createLibraryImport("QtQuick.Studio.Components", "1.0");
+ return context.view()->model()->isImportPossible(import, true, true);
+}
+
bool singleSelectedAndUiFile(const SelectionContext &context)
{
if (!singleSelection(context))
@@ -853,15 +875,31 @@ void DesignerActionManager::createDefaultDesignerActions()
priorityLayoutCategory,
&layoutOptionVisible));
- //isFlowTransitionItem
+ addDesignerAction(new ActionGroup(
+ groupCategoryDisplayName,
+ groupCategory,
+ priorityGroupCategory,
+ &positionOptionVisible,
+ &studioComponentsAvailable));
addDesignerAction(new ActionGroup(
flowCategoryDisplayName,
flowCategory,
priorityFlowCategory,
- &isFlowItemOrTransition,
+ &isFlowTargetOrTransition,
&flowOptionVisible));
+
+ auto effectMenu = new ActionGroup(
+ flowEffectCategoryDisplayName,
+ flowEffectCategory,
+ priorityFlowCategory,
+ &isFlowTransitionItem,
+ &flowOptionVisible);
+
+ effectMenu->setCategory(flowCategory);
+ addDesignerAction(effectMenu);
+
addDesignerAction(new ModelNodeFormEditorAction(
createFlowActionAreaCommandId,
createFlowActionAreaDisplayName,
@@ -874,26 +912,41 @@ void DesignerActionManager::createDefaultDesignerActions()
&isFlowItem,
&flowOptionVisible));
+ addDesignerAction(new ModelNodeContextMenuAction(
+ setFlowStartCommandId,
+ setFlowStartDisplayName,
+ {},
+ flowCategory,
+ priorityFirst,
+ {},
+ &setFlowStartItem,
+ &isFlowItem,
+ &flowOptionVisible));
+
addDesignerAction(new FlowActionConnectAction(
flowConnectionCategoryDisplayName,
flowConnectionCategory,
priorityFlowCategory));
- const QList<TypeName> types = {"FlowActionArea",
- "FlowFadeEffect",
- "FlowPushRightEffect",
- "FlowPushLeftEffect",
- "FlowPushUpEffect",
- "FlowSlideDownEffect",
- "FlowSlideLeftEffect",
- "FlowSlideRightEffect",
- "FlowSlideUpEffect",
+ const QList<TypeName> transitionTypes = {"FlowFadeEffect",
+ "FlowPushEffect",
+ "FlowMoveEffect",
"None"};
- for (const TypeName &typeName : types)
+ for (const TypeName &typeName : transitionTypes)
addTransitionEffectAction(typeName);
+ addDesignerAction(new ModelNodeContextMenuAction(
+ selectFlowEffectCommandId,
+ selectEffectDisplayName,
+ {},
+ flowCategory,
+ {},
+ priorityFlowCategory,
+ &selectFlowEffect,
+ &isFlowTransitionItemWithEffect));
+
addDesignerAction(new ActionGroup(
stackedContainerCategoryDisplayName,
stackedContainerCategory,
@@ -968,6 +1021,18 @@ void DesignerActionManager::createDefaultDesignerActions()
&isLayout,
&isLayout));
+ addDesignerAction(new ModelNodeContextMenuAction(
+ addToGroupItemCommandId,
+ addToGroupItemDisplayName,
+ {},
+ groupCategory,
+ QKeySequence(),
+ 110,
+ &addToGroupItem,
+ &selectionCanBeLayouted,
+ &selectionCanBeLayouted));
+
+
addDesignerAction(new ModelNodeFormEditorAction(
addItemToStackedContainerCommandId,
addItemToStackedContainerDisplayName,
@@ -1175,7 +1240,7 @@ void DesignerActionManager::addTransitionEffectAction(const TypeName &typeName)
QByteArray(ComponentCoreConstants::flowAssignEffectCommandId) + typeName,
QLatin1String(ComponentCoreConstants::flowAssignEffectDisplayName) + typeName,
{},
- ComponentCoreConstants::flowCategory,
+ ComponentCoreConstants::flowEffectCategory,
{},
typeName == "None" ? 100 : 140,
[typeName](const SelectionContext &context)
@@ -1186,6 +1251,10 @@ void DesignerActionManager::addTransitionEffectAction(const TypeName &typeName)
DesignerActionToolBar::DesignerActionToolBar(QWidget *parentWidget) : Utils::StyledBar(parentWidget),
m_toolBar(new QToolBar("ActionToolBar", this))
{
+ QWidget* empty = new QWidget();
+ empty->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
+ m_toolBar->addWidget(empty);
+
m_toolBar->setContentsMargins(0, 0, 0, 0);
m_toolBar->setFloatable(true);
m_toolBar->setMovable(true);
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp
index 6876684504..8a6c06fcdc 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp
@@ -25,6 +25,8 @@
#include "designeractionmanagerview.h"
+#include <customnotifications.h>
+
#include <selectioncontext.h>
#include <actioninterface.h>
#include <variantproperty.h>
@@ -53,7 +55,7 @@ void DesignerActionManagerView::modelAboutToBeDetached(Model *model)
void DesignerActionManagerView::nodeCreated(const ModelNode &)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::NodeCreated);
}
void DesignerActionManagerView::nodeRemoved(const ModelNode &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags)
@@ -63,17 +65,17 @@ void DesignerActionManagerView::nodeRemoved(const ModelNode &, const NodeAbstrac
void DesignerActionManagerView::nodeAboutToBeReparented(const ModelNode &, const NodeAbstractProperty &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::NodeHierachy);
}
void DesignerActionManagerView::nodeReparented(const ModelNode &, const NodeAbstractProperty &, const NodeAbstractProperty &, AbstractView::PropertyChangeFlags)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::NodeHierachy);
}
void DesignerActionManagerView::propertiesRemoved(const QList<AbstractProperty> &)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
}
void DesignerActionManagerView::rootNodeTypeChanged(const QString &, int, int)
@@ -112,7 +114,7 @@ void DesignerActionManagerView::selectedNodesChanged(const QList<ModelNode> &sel
void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &, const ModelNode &, int)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::NodeHierachy);
}
void DesignerActionManagerView::importsChanged(const QList<Import> &, const QList<Import> &)
@@ -122,27 +124,38 @@ void DesignerActionManagerView::importsChanged(const QList<Import> &, const QLis
void DesignerActionManagerView::signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &, AbstractView::PropertyChangeFlags)
{
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
}
void DesignerActionManagerView::variantPropertiesChanged(const QList<VariantProperty> &, AbstractView::PropertyChangeFlags propertyChangeFlag)
{
if (propertyChangeFlag == AbstractView::PropertiesAdded)
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
else if (hasSingleSelectedModelNode())
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
}
void DesignerActionManagerView::bindingPropertiesChanged(const QList<BindingProperty> &, AbstractView::PropertyChangeFlags propertyChangeFlag)
{
if (propertyChangeFlag == AbstractView::PropertiesAdded)
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
}
void DesignerActionManagerView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &)
{
if (hasSingleSelectedModelNode())
- setupContext(SelectionContext::UpdateMode::Fast);
+ setupContext(SelectionContext::UpdateMode::Properties);
+}
+
+void DesignerActionManagerView::customNotification(const AbstractView * /*view*/,
+ const QString &identifier,
+ const QList<ModelNode> & /* nodeList */,
+ const QList<QVariant> & /*data */)
+{
+ if (identifier == StartRewriterAmend)
+ m_isInRewriterTransaction = true;
+ else if (identifier == EndRewriterAmend)
+ m_isInRewriterTransaction = false;
}
DesignerActionManager &DesignerActionManagerView::designerActionManager()
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h
index 5eb0efe542..a7a34271ea 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h
@@ -71,6 +71,10 @@ public:
void emitSelectionChanged();
void setupContext(SelectionContext::UpdateMode updateMode = SelectionContext::UpdateMode::Normal);
+ void customNotification(const AbstractView *,
+ const QString &identifier,
+ const QList<ModelNode> &,
+ const QList<QVariant> &) override;
signals:
void selectionChanged(bool itemsSelected, bool rootItemIsSelected);
diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
index a1629c0b5b..e106fc9357 100644
--- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
@@ -167,7 +167,6 @@ void LayoutInGridLayout::doIt()
const TypeName layoutType = "QtQuick.Layouts.GridLayout";
if (!m_selectionContext.view()
- || !m_selectionContext.hasSingleSelectedModelNode()
|| !m_selectionContext.view()->model()->hasNodeMetaInfo(layoutType))
return;
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
index 67a53ef997..59029400e5 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
@@ -132,16 +132,22 @@ public:
bool isVisible(const SelectionContext &m_selectionState) const override { return m_visibility(m_selectionState); }
bool isEnabled(const SelectionContext &m_selectionState) const override { return m_enabled(m_selectionState); }
- QByteArray category() const override { return QByteArray(); }
+ QByteArray category() const override { return m_category; }
QByteArray menuId() const override { return m_menuId; }
int priority() const override { return m_priority; }
Type type() const override { return ContextMenu; }
+ void setCategory(const QByteArray &catageoryId)
+ {
+ m_category = catageoryId;
+ }
+
private:
const QByteArray m_menuId;
const int m_priority;
SelectionContextPredicate m_enabled;
SelectionContextPredicate m_visibility;
+ QByteArray m_category;
};
class SeperatorDesignerAction : public AbstractAction
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 2c239a8aa9..00e2ac028a 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -311,8 +311,10 @@ void resetSize(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetSize",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
- itemNode.removeProperty("width");
- itemNode.removeProperty("height");
+ if (itemNode.isValid()) {
+ itemNode.removeProperty("width");
+ itemNode.removeProperty("height");
+ }
}
});
}
@@ -325,8 +327,10 @@ void resetPosition(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetPosition",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
- itemNode.removeProperty("x");
- itemNode.removeProperty("y");
+ if (itemNode.isValid()) {
+ itemNode.removeProperty("x");
+ itemNode.removeProperty("y");
+ }
}
});
}
@@ -348,7 +352,8 @@ void resetZ(const SelectionContext &selectionState)
selectionState.view()->executeInTransaction("DesignerActionManager|resetZ",[selectionState](){
foreach (ModelNode node, selectionState.selectedModelNodes()) {
QmlItemNode itemNode(node);
- itemNode.removeProperty("z");
+ if (itemNode.isValid())
+ itemNode.removeProperty("z");
}
});
}
@@ -452,7 +457,6 @@ static void layoutHelperFunction(const SelectionContext &selectionContext,
const LessThan &lessThan)
{
if (!selectionContext.view()
- || !selectionContext.hasSingleSelectedModelNode()
|| !selectionContext.view()->model()->hasNodeMetaInfo(layoutType))
return;
@@ -1095,7 +1099,122 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ
container.nodeProperty("effect").reparentHere(effectNode);
view->setSelectedModelNode(effectNode);
}
- });
+ });
+}
+
+void setFlowStartItem(const SelectionContext &selectionContext)
+{
+ AbstractView *view = selectionContext.view();
+
+ QTC_ASSERT(view && selectionContext.hasSingleSelectedModelNode(), return);
+ ModelNode node = selectionContext.currentSingleSelectedNode();
+ QTC_ASSERT(node.isValid(), return);
+ QTC_ASSERT(node.metaInfo().isValid(), return);
+ QmlFlowItemNode flowItem(node);
+ QTC_ASSERT(flowItem.isValid(), return);
+ QTC_ASSERT(flowItem.flowView().isValid(), return);
+ view->executeInTransaction("DesignerActionManager:setFlowStartItem",
+ [&flowItem](){
+ flowItem.flowView().setStartFlowItem(flowItem);
+ });
+}
+
+
+bool static hasStudioComponentsImport(const SelectionContext &context)
+{
+ if (context.view() && context.view()->model()) {
+ Import import = Import::createLibraryImport("QtQuick.Studio.Components", "1.0");
+ return context.view()->model()->hasImport(import, true, true);
+ }
+
+ return false;
+}
+
+static inline void setAdjustedPos(const QmlDesigner::ModelNode &modelNode)
+{
+ if (modelNode.hasParentProperty()) {
+ ModelNode parentNode = modelNode.parentProperty().parentModelNode();
+
+ const QPointF instancePos = QmlItemNode(modelNode).instancePosition();
+ const int x = instancePos.x() - parentNode.variantProperty("x").value().toInt();
+ const int y = instancePos.y() - parentNode.variantProperty("y").value().toInt();
+
+ modelNode.variantProperty("x").setValue(x);
+ modelNode.variantProperty("y").setValue(y);
+ }
+}
+
+void reparentToNodeAndAdjustPosition(const ModelNode &parentModelNode,
+ const QList<ModelNode> &modelNodeList)
+{
+ for (ModelNode modelNode : modelNodeList) {
+ reparentTo(modelNode, parentModelNode);
+ setAdjustedPos(modelNode);
+
+ for (const VariantProperty &variantProperty : modelNode.variantProperties()) {
+ if (variantProperty.name().contains("anchors."))
+ modelNode.removeProperty(variantProperty.name());
+ }
+ for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) {
+ if (bindingProperty.name().contains("anchors."))
+ modelNode.removeProperty(bindingProperty.name());
+ }
+ }
+}
+
+void addToGroupItem(const SelectionContext &selectionContext)
+{
+ const TypeName typeName = "QtQuick.Studio.Components.GroupItem";
+
+ try {
+ if (!hasStudioComponentsImport(selectionContext)) {
+ Import studioImport = Import::createLibraryImport("QtQuick.Studio.Components", "1.0");
+ selectionContext.view()-> model()->changeImports({studioImport}, {});
+ }
+
+ if (!selectionContext.view())
+ return;
+
+ if (QmlItemNode::isValidQmlItemNode(selectionContext.firstSelectedModelNode())) {
+ const QmlItemNode qmlItemNode = QmlItemNode(selectionContext.firstSelectedModelNode());
+
+ if (qmlItemNode.hasInstanceParentItem()) {
+ ModelNode groupNode;
+ selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem1",[=, &groupNode](){
+
+ QmlItemNode parentNode = qmlItemNode.instanceParentItem();
+ NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName);
+ groupNode = selectionContext.view()->createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
+ reparentTo(groupNode, parentNode);
+ });
+ selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem2",[=](){
+
+ QList<ModelNode> selectedNodes = selectionContext.selectedModelNodes();
+ setUpperLeftPostionToNode(groupNode, selectedNodes);
+
+ reparentToNodeAndAdjustPosition(groupNode, selectedNodes);
+ });
+ }
+ }
+ } catch (RewritingException &e) {
+ e.showException();
+ }
+}
+
+void selectFlowEffect(const SelectionContext &selectionContext)
+{
+ if (!selectionContext.singleNodeIsSelected())
+ return;
+
+ ModelNode node = selectionContext.currentSingleSelectedNode();
+ QmlVisualNode transition(node);
+
+ QTC_ASSERT(transition.isValid(), return);
+ QTC_ASSERT(transition.isFlowTransition(), return);
+
+ if (node.hasNodeProperty("effect")) {
+ selectionContext.view()->setSelectedModelNode(node.nodeProperty("effect").modelNode());
+ }
}
} // namespace Mode
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index 994110297e..220afe9d4e 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -77,6 +77,9 @@ bool addFontToProject(const QStringList &fileNames, const QString &directory);
void createFlowActionArea(const SelectionContext &selectionContext);
void addTransition(const SelectionContext &selectionState);
void addFlowEffect(const SelectionContext &selectionState, const TypeName &typeName);
+void setFlowStartItem(const SelectionContext &selectionContext);
+void addToGroupItem(const SelectionContext &selectionContext);
+void selectFlowEffect(const SelectionContext &selectionContext);
} // namespace ModelNodeOperationso
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp b/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp
index 59a9454b09..80201b315b 100644
--- a/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/selectioncontext.cpp
@@ -124,12 +124,17 @@ bool SelectionContext::isValid() const
bool SelectionContext::fastUpdate() const
{
- return m_updateMode == UpdateMode::Fast;
+ return m_updateReason != UpdateMode::Normal;
}
void SelectionContext::setUpdateMode(UpdateMode mode)
{
- m_updateMode = mode;
+ m_updateReason = mode;
+}
+
+SelectionContext::UpdateMode SelectionContext::updateReason() const
+{
+ return m_updateReason;
}
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/selectioncontext.h b/src/plugins/qmldesigner/components/componentcore/selectioncontext.h
index 929bf4eebb..3fe2157788 100644
--- a/src/plugins/qmldesigner/components/componentcore/selectioncontext.h
+++ b/src/plugins/qmldesigner/components/componentcore/selectioncontext.h
@@ -35,7 +35,14 @@ namespace QmlDesigner {
class QMLDESIGNERCORE_EXPORT SelectionContext {
public:
- enum class UpdateMode {Normal, Fast};
+ enum class UpdateMode {
+ Normal,
+ Fast,
+ Properties,
+ NodeCreated,
+ NodeHierachy,
+ Selection
+ };
SelectionContext();
SelectionContext(AbstractView *view);
@@ -68,13 +75,15 @@ public:
bool fastUpdate() const;
void setUpdateMode(UpdateMode mode);
+ UpdateMode updateReason() const;
+
private:
QPointer<AbstractView> m_view;
ModelNode m_targetNode;
QPointF m_scenePosition;
bool m_showSelectionTools = false;
bool m_toggled = false;
- UpdateMode m_updateMode = UpdateMode::Normal;
+ UpdateMode m_updateReason = UpdateMode::Normal;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp
index ec4128479f..29b937a502 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp
@@ -28,19 +28,46 @@
#include <qmldesignerplugin.h>
+#include <coreplugin/icore.h>
+
#include <utils/stylehelper.h>
#include <QApplication>
#include <QRegExp>
#include <QScreen>
#include <QPointer>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QQmlProperty>
#include <qqml.h>
+static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg)
+
namespace QmlDesigner {
Theme::Theme(Utils::Theme *originTheme, QObject *parent)
: Utils::Theme(originTheme, parent)
+ , m_constants(nullptr)
{
+ QString constantsPath = Core::ICore::resourcePath() +
+ QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml");
+
+ QQmlEngine* engine = new QQmlEngine(this);
+ QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath));
+
+ if (component.status() == QQmlComponent::Ready) {
+ m_constants = component.create();
+ }
+ else if (component.status() == QQmlComponent::Error ) {
+ qCWarning(themeLog) << "Couldn't load" << constantsPath
+ << "due to the following error(s):";
+ for (QQmlError error : component.errors())
+ qCWarning(themeLog) << error.toString();
+ }
+ else {
+ qCWarning(themeLog) << "Couldn't load" << constantsPath
+ << "the status of the QQmlComponent is" << component.status();
+ }
}
QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName)
@@ -129,6 +156,25 @@ QPixmap Theme::getPixmap(const QString &id)
return QmlDesignerIconProvider::getPixmap(id);
}
+QString Theme::getIconUnicode(Theme::Icon i)
+{
+ if (!instance()->m_constants)
+ return QString();
+
+ const QMetaObject *m = instance()->metaObject();
+ const char *enumName = "Icon";
+ int enumIndex = m->indexOfEnumerator(enumName);
+
+ if (enumIndex == -1) {
+ qCWarning(themeLog) << "Couldn't find enum" << enumName;
+ return QString();
+ }
+
+ QMetaEnum e = m->enumerator(enumIndex);
+
+ return instance()->m_constants->property(e.valueToKey(i)).toString();
+}
+
QColor Theme::qmlDesignerBackgroundColorDarker() const
{
return getColor(QmlDesigner_BackgroundColorDarker);
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index 6940c0c1cf..4cc4deb298 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -41,12 +41,95 @@ namespace QmlDesigner {
class QMLDESIGNERCORE_EXPORT Theme : public Utils::Theme
{
Q_OBJECT
+
+ Q_ENUMS(Icon)
+
public:
+ enum Icon {
+ actionIcon,
+ actionIconBinding,
+ addColumnAfter,
+ addColumnBefore,
+ addFile,
+ addRowAfter,
+ addRowBefore,
+ addTable,
+ adsClose,
+ adsDetach,
+ adsDropDown,
+ aliasAnimated,
+ aliasProperty,
+ alignBottom,
+ alignCenterHorizontal,
+ alignCenterVertical,
+ alignLeft,
+ alignRight,
+ alignTo,
+ alignTop,
+ assign,
+ anchorBaseline,
+ anchorBottom,
+ anchorFill,
+ anchorLeft,
+ anchorRight,
+ anchorTop,
+ annotationBubble,
+ annotationDecal,
+ centerHorizontal,
+ centerVertical,
+ curveEditor,
+ closeCross,
+ decisionNode,
+ deleteColumn,
+ deleteRow,
+ deleteTable,
+ detach,
+ distributeBottom,
+ distributeCenterHorizontal,
+ distributeCenterVertical,
+ distributeLeft,
+ distributeOriginBottomRight,
+ distributeOriginCenter,
+ distributeOriginNone,
+ distributeOriginTopLeft,
+ distributeRight,
+ distributeSpacingHorizontal,
+ distributeSpacingVertical,
+ distributeTop,
+ edit,
+ fontStyleBold,
+ fontStyleItalic,
+ fontStyleStrikethrough,
+ fontStyleUnderline,
+ mergeCells,
+ redo,
+ splitColumns,
+ splitRows,
+ startNode,
+ testIcon,
+ textAlignBottom,
+ textAlignCenter,
+ textAlignLeft,
+ textAlignMiddle,
+ textAlignRight,
+ textAlignTop,
+ textBulletList,
+ textFullJustification,
+ textNumberedList,
+ tickIcon,
+ triState,
+ undo,
+ upDownIcon,
+ upDownSquare2,
+ wildcard
+ };
+
static Theme *instance();
static QString replaceCssColors(const QString &input);
static void setupTheme(QQmlEngine *engine);
static QColor getColor(Color role);
static QPixmap getPixmap(const QString &id);
+ static QString getIconUnicode(Theme::Icon i);
Q_INVOKABLE QColor qmlDesignerBackgroundColorDarker() const;
Q_INVOKABLE QColor qmlDesignerBackgroundColorDarkAlternate() const;
@@ -58,9 +141,12 @@ public:
Q_INVOKABLE int smallFontPixelSize() const;
Q_INVOKABLE int captionFontPixelSize() const;
Q_INVOKABLE bool highPixelDensity() const;
+
private:
Theme(Utils::Theme *originTheme, QObject *parent);
QColor evaluateColorAtThemeInstance(const QString &themeColorName);
+
+ QObject *m_constants;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index c8e1e222e5..9e882be2fd 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -179,6 +179,17 @@ void Edit3DView::importsChanged(const QList<Import> &addedImports,
checkImports();
}
+void Edit3DView::customNotification(const AbstractView *view, const QString &identifier,
+ const QList<ModelNode> &nodeList, const QList<QVariant> &data)
+{
+ Q_UNUSED(view)
+ Q_UNUSED(nodeList)
+ Q_UNUSED(data)
+
+ if (identifier == "asset_import_update")
+ resetPuppet();
+}
+
void Edit3DView::sendInputEvent(QInputEvent *e) const
{
if (nodeInstanceView())
@@ -301,14 +312,16 @@ QVector<Edit3DAction *> Edit3DView::rightActions() const
void Edit3DView::addQuick3DImport()
{
- const QList<Import> imports = model()->possibleImports();
- for (const auto &import : imports) {
- if (import.url() == "QtQuick3D") {
- model()->changeImports({import}, {});
-
- // Subcomponent manager update needed to make item library entries appear
- QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
- return;
+ if (model()) {
+ const QList<Import> imports = model()->possibleImports();
+ for (const auto &import : imports) {
+ if (import.url() == "QtQuick3D") {
+ model()->changeImports({import}, {});
+
+ // Subcomponent manager update needed to make item library entries appear
+ QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
+ return;
+ }
}
}
Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"),
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index d1646470b7..6c3ae892a9 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -58,6 +58,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
+ void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void sendInputEvent(QInputEvent *e) const;
void edit3DViewResized(const QSize &size) const;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index c948227233..3d0d3f2f64 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -106,12 +106,11 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
// Onboarding label contains instructions for new users how to get 3D content into the project
m_onboardingLabel = new QLabel(this);
QString labelText =
- "No 3D import here yet!<br><br>"
- "To create a 3D View you need to add the QtQuick3D import to your file.<br>"
- "You can add the import via the QML Imports tab of the Library view, or alternatively click"
- " <a href=\"#add_import\"><span style=\"text-decoration:none;color:%1\">here</span></a> "
- "to add it straight away.<br><br>"
- "If you want to import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view.";
+ tr("Your file does not import Qt Quick 3D.<br><br>"
+ "To create a 3D view, add the QtQuick3D import to your file in the QML Imports tab of the Library view. Or click"
+ " <a href=\"#add_import\"><span style=\"text-decoration:none;color:%1\">here</span></a> "
+ "here to add it immediately.<br><br>"
+ "To import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view.");
m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name()));
m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
connect(m_onboardingLabel, &QLabel::linkActivated, this, &Edit3DWidget::linkActivated);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri
index 3464ba3afe..4609b277f9 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditor.pri
+++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri
@@ -36,7 +36,8 @@ SOURCES += formeditoritem.cpp \
contentnoteditableindicator.cpp \
backgroundaction.cpp \
formeditortoolbutton.cpp \
- formeditorannotationicon.cpp
+ formeditorannotationicon.cpp \
+ transitiontool.cpp
HEADERS += formeditorscene.h \
formeditorwidget.h \
@@ -75,6 +76,7 @@ HEADERS += formeditorscene.h \
contentnoteditableindicator.h \
backgroundaction.h \
formeditortoolbutton.h \
- formeditorannotationicon.h
+ formeditorannotationicon.h \
+ transitiontool.h
RESOURCES += formeditor.qrc
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 96a4cc5adf..13ab98daff 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -26,16 +26,20 @@
#include "formeditoritem.h"
#include "formeditorscene.h"
+#include <variantproperty.h>
#include <bindingproperty.h>
#include <modelnode.h>
#include <nodehints.h>
#include <nodemetainfo.h>
+#include <theme.h>
+
#include <utils/theme/theme.h>
#include <utils/qtcassert.h>
#include <QDebug>
+#include <QFontDatabase>
#include <QPainter>
#include <QPainterPath>
#include <QStyleOptionGraphicsItem>
@@ -47,6 +51,36 @@
namespace QmlDesigner {
const int flowBlockSize = 200;
+const int blockRadius = 18;
+const int blockAdjust = 40;
+
+const char startNodeIcon[] = "\u0055";
+
+void drawIcon(QPainter *painter,
+ int x,
+ int y,
+ const QString &iconSymbol,
+ int fontSize, int iconSize,
+ const QColor &penColor)
+{
+ static QFontDatabase a;
+
+ const QString fontName = "qtds_propertyIconFont.ttf";
+
+ Q_ASSERT(a.hasFamily(fontName));
+
+ if (a.hasFamily(fontName)) {
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
+
+ painter->save();
+ painter->setPen(penColor);
+ painter->setFont(font);
+ painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
+
+ painter->restore();
+ }
+}
FormEditorScene *FormEditorItem::scene() const {
return qobject_cast<FormEditorScene*>(QGraphicsItem::scene());
@@ -419,6 +453,7 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
}
}
+ painter->setClipping(false);
if (!qmlItemNode().isRootModelNode())
paintBoundingRect(painter);
@@ -578,6 +613,7 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi
return;
painter->save();
+ painter->setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setJoinStyle(Qt::MiterJoin);
@@ -622,10 +658,9 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi
fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value<QColor>();
if (fillColor.alpha() > 0)
- painter->fillRect(boundingRect(), fillColor);
-
- painter->drawRect(boundingRect());
+ painter->setBrush(fillColor);
+ painter->drawRoundedRect(boundingRect(), blockRadius, blockRadius);
painter->restore();
}
@@ -719,7 +754,7 @@ void FormEditorTransitionItem::updateGeometry()
QPointF toP = QmlItemNode(resolved.to).flowPosition();
if (QmlItemNode(resolved.to).isFlowDecision())
- sizeTo = QRectF(0, 0, flowBlockSize, flowBlockSize);
+ sizeTo = QRectF(0, 0, flowBlockSize * 2, flowBlockSize * 2);
qreal x1 = fromP.x();
qreal x2 = toP.x();
@@ -757,10 +792,10 @@ QPointF FormEditorTransitionItem::instancePosition() const
static bool verticalOverlap(const QRectF &from, const QRectF &to)
{
- if (from.top() < to.bottom() && (from.top() + from.height()) > to.top())
+ if (from.top() < to.bottom() && from.bottom() > to.top())
return true;
- if (to.top() < from.bottom() && (to.top() + to.height()) > from.top())
+ if (to.top() < from.bottom() && to.bottom() > from.top())
return true;
return false;
@@ -769,25 +804,270 @@ static bool verticalOverlap(const QRectF &from, const QRectF &to)
static bool horizontalOverlap(const QRectF &from, const QRectF &to)
{
- if (from.left() < to.right() && (from.left() + from.width()) > to.left())
+ if (from.left() < to.right() && from.right() > to.left())
return true;
- if (to.left() < from.right() && (to.left() + to.width()) > from.left())
+ if (to.left() < from.right() && to.right() > from.left())
return true;
return false;
}
+static void drawLabel(QPainter *painter,
+ const QPainterPath &path,
+ const QString &text,
+ const qreal percent,
+ const qreal offset,
+ bool flipSide)
+{
+ if (text.isEmpty())
+ return;
+
+ const int flags = Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextDontClip;
+
+ QPointF pos = path.pointAtPercent(percent);
+ qreal angle = path.angleAtPercent(percent);
+
+ QLineF tmp(pos, QPointF(10, 10));
+ tmp.setLength(offset);
+ tmp.setAngle(angle + (flipSide ? 270 : 90));
+
+ QRectF textRect(0, 0, 100, 50);
+ textRect.moveCenter(tmp.p2());
+
+ auto normalizeAngle = [](int angle) {
+ int newAngle = angle;
+ while (newAngle <= -90) newAngle += 180;
+ while (newAngle > 90) newAngle -= 180;
+ return newAngle;
+ };
+
+ painter->save();
+ painter->translate(textRect.center());
+ painter->rotate(-normalizeAngle(angle));
+ painter->translate(-textRect.center());
+ painter->drawText(textRect, flags, text);
+ painter->restore();
+}
+
+static void drawArrow(QPainter *painter,
+ const QPointF &point,
+ const qreal &angle,
+ int arrowLength,
+ int arrowWidth)
+{
+ const QPointF peakP(0, 0);
+ const QPointF leftP(-arrowLength, -arrowWidth * 0.5);
+ const QPointF rightP(-arrowLength, arrowWidth * 0.5);
+
+ painter->save();
+
+ painter->translate(point);
+ painter->rotate(-angle);
+ painter->drawLine(leftP, peakP);
+ painter->drawLine(rightP, peakP);
+
+ painter->restore();
+}
+
+static QPainterPath roundedCorner(const QPointF &s,
+ const QPointF &m,
+ const QPointF &e,
+ int radius)
+{
+ const QVector2D sm(m - s);
+ const QVector2D me(e - m);
+ const float smLength = sm.length();
+ const float meLength = me.length();
+ const int actualRadius = qMin(radius, static_cast<int>(qMin(smLength, meLength)));
+ const QVector2D smNorm = sm.normalized();
+ const QVector2D meNorm = me.normalized();
+ QRectF rect(m, QSizeF(actualRadius * 2, actualRadius * 2));
+
+ QPainterPath path(s);
+
+ if (smNorm.y() < 0 && meNorm.x() > 0) {
+ rect.moveTopLeft(m);
+ path.arcTo(rect, 180, -90);
+ } else if (smNorm.x() < 0 && meNorm.y() > 0) {
+ rect.moveTopLeft(m);
+ path.arcTo(rect, 90, 90);
+ } else if (smNorm.y() > 0 && meNorm.x() > 0) {
+ rect.moveBottomLeft(m);
+ path.arcTo(rect, 180, 90);
+ } else if (smNorm.x() < 0 && meNorm.y() < 0) {
+ rect.moveBottomLeft(m);
+ path.arcTo(rect, 270, -90);
+ } else if (smNorm.x() > 0 && meNorm.y() > 0) {
+ rect.moveTopRight(m);
+ path.arcTo(rect, 90, -90);
+ } else if (smNorm.y() < 0 && meNorm.x() < 0) {
+ rect.moveTopRight(m);
+ path.arcTo(rect, 0, 90);
+ } else if (smNorm.y() > 0 && meNorm.x() < 0) {
+ rect.moveBottomRight(m);
+ path.arcTo(rect, 0, -90);
+ } else if (smNorm.x() > 0 && meNorm.y() < 0) {
+ rect.moveBottomRight(m);
+ path.arcTo(rect, 270, 90);
+ }
+
+ path.lineTo(e);
+ return path;
+}
+
+// This function determines whether the vertices are in cw or ccw order.
+// It finds the lowest and rightmost vertex, and computes the cross-product
+// of the vectors along its incident edges.
+// Written by Joseph O'Rourke, 25 August 1995. orourke@cs.smith.edu
+// 1: ccw
+// 0: default
+// -1: cw
+
+static int counterClockWise(const std::vector<QPointF> &points)
+{
+ if (points.empty())
+ return 0;
+
+ // FindLR finds the lowest, rightmost point.
+ auto findLR = [](const std::vector<QPointF> &points) {
+ int i = 0;
+ int m = 0;
+ QPointF min = points.front();
+
+ for (const auto p : points) {
+ if ((p.y() < min.y()) || ((p.y() == min.y()) && (p.x() > min.x()))) {
+ m = i;
+ min = p;
+ }
+ ++i;
+ }
+ return m;
+ };
+
+ const int m = findLR(points);
+ const int n = points.size();
+
+ // Determine previous and next point to m (the lowest, rightmost point).
+ const QPointF a = points[(m + (n - 1)) % n];
+ const QPointF b = points[m];
+ const QPointF c = points[(m + 1) % n];
+
+ const int area = a.x() * b.y() - a.y() * b.x() +
+ a.y() * c.x() - a.x() * c.y() +
+ b.x() * c.y() - c.x() * b.y();
+
+ if (area > 0)
+ return 1;
+ else if (area < 0)
+ return -1;
+ else
+ return 0;
+}
+
+static QPainterPath quadBezier(const QPointF &s,
+ const QPointF &c,
+ const QPointF &e,
+ int bezier,
+ int breakOffset)
+{
+ QLineF se(s, e);
+ QPointF breakPoint = se.pointAt(breakOffset / 100.0);
+ QLineF breakLine;
+
+ if (counterClockWise({s, c, e}) == 1)
+ breakLine = QLineF(breakPoint, breakPoint + QPointF(se.dy(), -se.dx()));
+ else
+ breakLine = QLineF(breakPoint, breakPoint + QPointF(-se.dy(), se.dx()));
+
+ breakLine.setLength(se.length());
+
+ const QPointF controlPoint = breakLine.pointAt(bezier / 100.0);
+
+ QPainterPath path(s);
+ path.quadTo(controlPoint, e);
+
+ return path;
+}
+
+static QPainterPath cubicBezier(const QPointF &s,
+ const QPointF &c1,
+ const QPointF &c2,
+ const QPointF &e,
+ int bezier)
+{
+ QPainterPath path(s);
+ const QPointF adjustedC1 = QLineF(s, c1).pointAt(bezier / 100.0);
+ const QPointF adjustedC2 = QLineF(e, c2).pointAt(bezier / 100.0);
+
+ path.cubicTo(adjustedC1, adjustedC2, e);
+
+ return path;
+}
+
+
+static QPainterPath lShapedConnection(const QPointF &start,
+ const QPointF &end,
+ Qt::Orientation orientation,
+ const ConnectionStyle &style)
+{
+ const QPointF mid = (orientation == Qt::Horizontal) ? QPointF(end.x(), start.y())
+ : QPointF(start.x(), end.y());
+
+ if (style.type == ConnectionType::Default) {
+ if (style.radius == 0) {
+ QPainterPath path(start);
+ path.lineTo(mid);
+ path.lineTo(end);
+ return path;
+ } else {
+ return roundedCorner(start, mid, end, style.radius);
+ }
+ } else {
+ return quadBezier(start, mid, end, style.bezier, style.breakOffset);
+ }
+}
+
+static QPainterPath sShapedConnection(const QPointF &start,
+ const QPointF &end,
+ Qt::Orientation orientation,
+ const ConnectionStyle &style)
+{
+ const qreal middleFactor = style.breakOffset / 100.0;
+ QPointF mid1;
+ QPointF mid2;
+
+ if (orientation == Qt::Horizontal) {
+ mid1 = QPointF(start.x() * middleFactor + end.x() * (1 - middleFactor), start.y());
+ mid2 = QPointF(mid1.x(), end.y());
+ } else {
+ mid1 = QPointF(start.x(), start.y() * middleFactor + end.y() * (1 - middleFactor));
+ mid2 = QPointF(end.x(), mid1.y());
+ }
+
+ if (style.type == ConnectionType::Default) {
+ if (style.radius == 0) {
+ QPainterPath path(start);
+ path.lineTo(mid1);
+ path.lineTo(mid2);
+ path.lineTo(end);
+ return path;
+ } else {
+ const QLineF breakLine(mid1, mid2);
+ QPainterPath path1 = roundedCorner(start, mid1, breakLine.center(), style.radius);
+ QPainterPath path2 = roundedCorner(breakLine.center(), mid2, end, style.radius);
+ return path1 + path2;
+ }
+ } else {
+ return cubicBezier(start, mid1, mid2, end, style.bezier);
+ }
+}
+
static void paintConnection(QPainter *painter,
const QRectF &from,
const QRectF &to,
- qreal width,
- qreal adjustedWidth,
- const QColor &color,
- bool dash,
- int startOffset,
- int endOffset,
- int breakOffset)
+ const ConnectionStyle &style,
+ const QString &label)
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
@@ -796,23 +1076,22 @@ static void paintConnection(QPainter *painter,
pen.setCosmetic(true);
pen.setJoinStyle(Qt::MiterJoin);
pen.setCapStyle(Qt::RoundCap);
+ pen.setColor(style.color);
- pen.setColor(color);
-
- if (dash)
+ if (style.dash)
pen.setStyle(Qt::DashLine);
else
pen.setStyle(Qt::SolidLine);
- pen.setWidthF(width);
+ pen.setWidthF(style.width);
painter->setPen(pen);
//const bool forceVertical = false;
//const bool forceHorizontal = false;
- const int padding = 2 * width + 2 * adjustedWidth;
+ const int padding = 2 * style.width + 2 * style.adjustedWidth;
- const int arrowLength = 4 * adjustedWidth;
- const int arrowWidth = 8 * adjustedWidth;
+ const int arrowLength = 4 * style.adjustedWidth;
+ const int arrowWidth = 8 * style.adjustedWidth;
const bool boolExitRight = from.right() < to.center().x();
const bool boolExitBottom = from.bottom() < to.center().y();
@@ -824,10 +1103,6 @@ static void paintConnection(QPainter *painter,
horizontalFirst = false;
*/
- const qreal middleFactor = breakOffset / 100.0;
-
- QPointF startP;
-
bool extraLine = false;
if (horizontalFirst) {
@@ -849,131 +1124,57 @@ static void paintConnection(QPainter *painter,
}
}
- if (horizontalFirst) {
- const qreal startY = from.center().y() + startOffset;
- qreal startX = from.x() - padding;
- if (boolExitRight)
- startX = from.right() + padding;
+ QPointF startP;
+ QPointF endP;
+ QPainterPath path;
+ if (horizontalFirst) {
+ const qreal startX = boolExitRight ? from.right() + padding : from.x() - padding;
+ const qreal startY = from.center().y() + style.outOffset;
startP = QPointF(startX, startY);
- qreal endY = to.top() - padding;
-
- if (from.bottom() > to.y())
- endY = to.bottom() + padding;
-
if (!extraLine) {
-
-
- const qreal endX = to.center().x() + endOffset;
-
- const QPointF midP(endX, startY);
-
- const QPointF endP(endX, endY);
-
- painter->drawLine(startP, midP);
- painter->drawLine(midP, endP);
-
- int flip = 1;
-
- if (midP.y() < endP.y())
- flip = -1;
-
- pen.setStyle(Qt::SolidLine);
- painter->setPen(pen);
- painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP);
- painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP);
+ const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding;
+ endP = QPointF(to.center().x() + style.inOffset, endY);
+ path = lShapedConnection(startP, endP, Qt::Horizontal, style);
} else {
-
- qreal endX = to.left() - padding;
-
- if (from.right() > to.x())
- endX = to.right() + padding;
-
- const qreal midX = startX * middleFactor + endX * (1-middleFactor);
- const QPointF midP(midX, startY);
- const QPointF midP2(midX, to.center().y() + endOffset);
- const QPointF endP(endX, to.center().y() + endOffset);
- painter->drawLine(startP, midP);
- painter->drawLine(midP, midP2);
- painter->drawLine(midP2, endP);
-
- int flip = 1;
-
- if (midP2.x() < endP.x())
- flip = -1;
-
- pen.setStyle(Qt::SolidLine);
- painter->setPen(pen);
- painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP);
- painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP);
+ const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding;
+ endP = QPointF(endX, to.center().y() + style.inOffset);
+ path = sShapedConnection(startP, endP, Qt::Horizontal, style);
}
-
} else {
- const qreal startX = from.center().x() + startOffset;
-
- qreal startY = from.top() - padding;
- if (boolExitBottom)
- startY = from.bottom() + padding;
-
+ const qreal startX = from.center().x() + style.outOffset;
+ const qreal startY = boolExitBottom ? from.bottom() + padding : from.top() - padding;
startP = QPointF(startX, startY);
- qreal endX = to.left() - padding;
-
- if (from.right() > to.x())
- endX = to.right() + padding;
if (!extraLine) {
- const qreal endY = to.center().y() + endOffset;
-
- const QPointF midP(startX, endY);
-
- const QPointF endP(endX, endY);
-
- painter->drawLine(startP, midP);
- painter->drawLine(midP, endP);
-
- int flip = 1;
-
- if (midP.x() < endP.x())
- flip = -1;
-
- pen.setStyle(Qt::SolidLine);
- painter->setPen(pen);
- painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowWidth / 2), endP);
- painter->drawLine(endP + flip * QPoint(arrowLength, -arrowWidth / 2), endP);
+ const qreal endX = (from.right() > to.x()) ? to.right() + padding : to.left() - padding;
+ endP = QPointF(endX, to.center().y() + style.inOffset);
+ path = lShapedConnection(startP, endP, Qt::Vertical, style);
} else {
+ const qreal endY = (from.bottom() > to.y()) ? to.bottom() + padding : to.top() - padding;
+ endP = QPointF(to.center().x() + style.inOffset, endY);
+ path = sShapedConnection(startP, endP, Qt::Vertical, style);
+ }
+ }
- qreal endY = to.top() - padding;
-
- if (from.bottom() > to.y())
- endY = to.bottom() + padding;
-
- const qreal midY = startY * middleFactor + endY * (1-middleFactor);
- const QPointF midP(startX, midY);
- const QPointF midP2(to.center().x() + endOffset, midY);
- const QPointF endP(to.center().x() + endOffset, endY);
+ painter->drawPath(path);
- painter->drawLine(startP, midP);
- painter->drawLine(midP, midP2);
- painter->drawLine(midP2, endP);
+ pen.setWidthF(style.width);
+ pen.setStyle(Qt::SolidLine);
+ painter->setPen(pen);
- int flip = 1;
+ qreal anglePercent = 1.0;
- if (midP2.y() < endP.y())
- flip = -1;
+ if (extraLine && style.bezier < 80)
+ anglePercent = 1.0 - qMin(1.0, (80 - style.bezier) / 10.0) * 0.05;
- pen.setStyle(Qt::SolidLine);
- painter->setPen(pen);
- painter->drawLine(endP + flip * QPoint(arrowWidth / 2, arrowLength), endP);
- painter->drawLine(endP + flip * QPoint(-arrowWidth / 2, arrowLength), endP);
- }
- }
+ drawArrow(painter, endP, path.angleAtPercent(anglePercent), arrowLength, arrowWidth);
- pen.setWidthF(width);
- pen.setStyle(Qt::SolidLine);
- painter->setPen(pen);
painter->setBrush(Qt::white);
- painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3);
+ painter->drawEllipse(startP, arrowLength / 3, arrowLength / 3);
+
+ drawLabel(painter, path, label, style.labelPosition / 100.0, style.labelOffset, style.labelFlipSide);
painter->restore();
}
@@ -991,6 +1192,8 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi
painter->save();
+ painter->setRenderHint(QPainter::Antialiasing);
+
ResolveConnection resolved(qmlItemNode());
if (!resolved.from.modelNode().isValid())
@@ -1022,77 +1225,133 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi
toRect.translate(QmlItemNode(resolved.to).flowPosition());
if (resolved.isStartLine) {
- fromRect = QRectF(0,0,50,50);
- fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-120, toRect.height() / 2 - 25));
+ fromRect = QRectF(0, 0, 96, 96);
+ fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-180, toRect.height() / 2 - 96 / 2));
}
toRect.translate(-pos());
fromRect.translate(-pos());
- qreal width = 2;
+ ConnectionStyle style;
+
+ style.width = 2;
const qreal scaleFactor = viewportTransform().m11();
if (qmlItemNode().modelNode().hasAuxiliaryData("width"))
- width = qmlItemNode().modelNode().auxiliaryData("width").toInt();
+ style.width = qmlItemNode().modelNode().auxiliaryData("width").toInt();
- qreal adjustedWidth = width / scaleFactor;
+ style.adjustedWidth = style.width / scaleFactor;
if (qmlItemNode().modelNode().isSelected())
- width += 2;
+ style.width += 2;
if (m_hitTest)
- width *= 8;
+ style.width *= 8;
- QColor color = "#e71919";
+ style.color = "#e71919";
if (resolved.isStartLine)
- color = "blue";
+ style.color = "blue";
if (resolved.isWildcardLine)
- color = "green";
-
- bool dash = false;
+ style.color = "green";
if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionColor"))
- color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>();
+ style.color = qmlItemNode().rootModelNode().auxiliaryData("transitionColor").value<QColor>();
if (qmlItemNode().modelNode().hasAuxiliaryData("color"))
- color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>();
+ style.color = qmlItemNode().modelNode().auxiliaryData("color").value<QColor>();
+
+ style.dash = false;
if (qmlItemNode().modelNode().hasAuxiliaryData("dash"))
- dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool();
+ style.dash = qmlItemNode().modelNode().auxiliaryData("dash").toBool();
- int outOffset = 0;
- int inOffset = 0;
+ style.outOffset = 0;
+ style.inOffset = 0;
if (qmlItemNode().modelNode().hasAuxiliaryData("outOffset"))
- outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt();
+ style.outOffset = qmlItemNode().modelNode().auxiliaryData("outOffset").toInt();
if (qmlItemNode().modelNode().hasAuxiliaryData("inOffset"))
- inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt();
+ style.inOffset = qmlItemNode().modelNode().auxiliaryData("inOffset").toInt();
- int breakOffset = 50;
+ style.breakOffset = 50;
if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint"))
- breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt();
+ style.breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt();
+
+ style.radius = 8;
+
+ if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionRadius"))
+ style.radius = qmlItemNode().rootModelNode().auxiliaryData("transitionRadius").toInt();
+
+ if (qmlItemNode().modelNode().hasAuxiliaryData("radius"))
+ style.radius = qmlItemNode().modelNode().auxiliaryData("radius").toInt();
+
+ style.bezier = 50;
+
+ if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionBezier"))
+ style.bezier = qmlItemNode().rootModelNode().auxiliaryData("transitionBezier").toInt();
+
+ if (qmlItemNode().modelNode().hasAuxiliaryData("bezier"))
+ style.bezier = qmlItemNode().modelNode().auxiliaryData("bezier").toInt();
+
+ style.type = ConnectionType::Default;
+
+ if (qmlItemNode().rootModelNode().hasAuxiliaryData("transitionType"))
+ style.type = static_cast<ConnectionType>(qmlItemNode().rootModelNode().auxiliaryData("transitionType").toInt());
+
+ if (qmlItemNode().modelNode().hasAuxiliaryData("type"))
+ style.type = static_cast<ConnectionType>(qmlItemNode().modelNode().auxiliaryData("type").toInt());
+
+
+ QFont font = painter->font();
+ font.setPixelSize(16 / scaleFactor);
+ painter->setFont(font);
+
+ QString label;
+
+ if (qmlItemNode().modelNode().hasBindingProperty("condition"))
+ label = qmlItemNode().modelNode().bindingProperty("condition").expression();
+
+ if (qmlItemNode().modelNode().hasVariantProperty("question"))
+ label = qmlItemNode().modelNode().variantProperty("question").value().toString();
- paintConnection(painter, fromRect, toRect, width, adjustedWidth ,color, dash, outOffset, inOffset, breakOffset);
+ style.labelOffset = 14 / scaleFactor;
+
+ style.labelPosition = 50.0;
+
+ if (qmlItemNode().modelNode().hasAuxiliaryData("labelPosition"))
+ style.labelPosition = qmlItemNode().modelNode().auxiliaryData("labelPosition").toReal();
+
+ style.labelFlipSide = false;
+
+ if (qmlItemNode().modelNode().hasAuxiliaryData("labelFlipSide"))
+ style.labelFlipSide = qmlItemNode().modelNode().auxiliaryData("labelFlipSide").toBool();
+
+ if (resolved.isStartLine)
+ fromRect.translate(0, style.outOffset);
+
+ paintConnection(painter, fromRect, toRect, style, label);
if (resolved.isStartLine) {
+
+ const QString icon = Theme::getIconUnicode(Theme::startNode);
+
QPen pen;
pen.setCosmetic(true);
-
- pen.setColor(color);
+ pen.setColor(style.color);
painter->setPen(pen);
- painter->drawRect(fromRect);
-
- if (scaleFactor > 0.4) {
- painter->drawLine(fromRect.topRight() + QPoint(20,10), fromRect.bottomRight() + QPoint(20,-10));
- painter->drawLine(fromRect.topRight() + QPoint(25,12), fromRect.bottomRight() + QPoint(25,-12));
- painter->drawLine(fromRect.topRight() + QPoint(30,15), fromRect.bottomRight() + QPoint(30,-15));
- painter->drawLine(fromRect.topRight() + QPoint(35,17), fromRect.bottomRight() + QPoint(35,-17));
- painter->drawLine(fromRect.topRight() + QPoint(40,20), fromRect.bottomRight() + QPoint(40,-20));
- }
+
+ const int iconAdjust = 48;
+ const int offset = 96;
+ const int size = fromRect.width();
+ const int iconSize = size - iconAdjust;
+ const int x = fromRect.topRight().x() - offset;
+ const int y = fromRect.topRight().y();
+ painter->drawRoundedRect(x, y , size - 10, size, size / 2, iconSize / 2);
+ drawIcon(painter, x + iconAdjust / 2, y + iconAdjust / 2, icon, iconSize, iconSize, style.color);
}
painter->restore();
@@ -1141,6 +1400,9 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap
painter->save();
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+
QPen pen;
pen.setJoinStyle(Qt::MiterJoin);
pen.setCosmetic(true);
@@ -1179,20 +1441,37 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap
if (qmlItemNode().modelNode().hasAuxiliaryData("fillColor"))
fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value<QColor>();
+ painter->save();
+
+ if (m_iconType == DecisionIcon) {
+ painter->translate(boundingRect().center());
+ painter->rotate(45);
+ painter->translate(-boundingRect().center());
+ }
+
if (fillColor.alpha() > 0)
- painter->fillRect(boundingRect(), fillColor);
+ painter->setBrush(fillColor);
+
+ int radius = blockRadius;
+
+ const QRectF adjustedRect = boundingRect().adjusted(blockAdjust,
+ blockAdjust,
+ -blockAdjust,
+ -blockAdjust);
- painter->drawLine(boundingRect().left(), boundingRect().center().y(),
- boundingRect().center().x(), boundingRect().top());
+ painter->drawRoundedRect(adjustedRect, radius, radius);
- painter->drawLine(boundingRect().center().x(), boundingRect().top(),
- boundingRect().right(), boundingRect().center().y());
+ const int iconDecrement = 32;
+ const int iconSize = adjustedRect.width() - iconDecrement;
+ const int offset = iconDecrement / 2 + blockAdjust;
+
+ painter->restore();
- painter->drawLine(boundingRect().right(), boundingRect().center().y(),
- boundingRect().center().x(), boundingRect().bottom());
+ const QString icon = (m_iconType ==
+ WildcardIcon) ? Theme::getIconUnicode(Theme::wildcard)
+ : Theme::getIconUnicode(Theme::decisionNode);
- painter->drawLine(boundingRect().center().x(), boundingRect().bottom(),
- boundingRect().left(), boundingRect().center().y());
+ drawIcon(painter, offset, offset, icon, iconSize, iconSize, flowColor);
painter->restore();
}
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
index d725afd0b0..a2a491fdbf 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
@@ -47,6 +47,30 @@ namespace Internal {
class MoveController;
}
+enum ConnectionType
+{
+ Default = 0,
+ Bezier
+};
+
+class ConnectionStyle
+{
+public:
+ qreal width;
+ qreal adjustedWidth;
+ QColor color;
+ bool dash;
+ int outOffset;
+ int inOffset;
+ int breakOffset;
+ int radius;
+ int bezier;
+ ConnectionType type;
+ qreal labelOffset;
+ qreal labelPosition;
+ bool labelFlipSide;
+};
+
class QMLDESIGNERCORE_EXPORT FormEditorItem : public QGraphicsItem
{
friend class QmlDesigner::FormEditorScene;
@@ -207,9 +231,17 @@ public:
bool flowHitTest(const QPointF &point) const override;
protected:
- FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene)
- : FormEditorFlowItem(qmlItemNode, scene)
+ enum IconType {
+ DecisionIcon,
+ WildcardIcon
+ };
+
+ FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode,
+ FormEditorScene* scene,
+ IconType iconType = DecisionIcon)
+ : FormEditorFlowItem(qmlItemNode, scene), m_iconType(iconType)
{}
+ IconType m_iconType;
};
class FormEditorFlowWildcardItem : FormEditorFlowDecisionItem
@@ -221,8 +253,9 @@ public:
protected:
FormEditorFlowWildcardItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene)
- : FormEditorFlowDecisionItem(qmlItemNode, scene)
- {}
+ : FormEditorFlowDecisionItem(qmlItemNode, scene, WildcardIcon)
+ {
+ }
};
inline int FormEditorItem::type() const
diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp
index 49bb98db4a..8907aab2bc 100644
--- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp
@@ -97,8 +97,11 @@ void ResizeManipulator::begin(const QPointF &/*beginPoint*/)
// return QSizeF(sizeAsPoint.x(), sizeAsPoint.y());
//}
-void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping)
+void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping, Qt::KeyboardModifiers keyMods)
{
+ const bool preserveAspectRatio = keyMods.testFlag(Qt::ShiftModifier);
+ const bool resizeFromCenter = keyMods.testFlag(Qt::AltModifier);
+
const double minimumWidth = 0.0;
const double minimumHeight = 0.0;
@@ -118,6 +121,16 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
QmlAnchors anchors(formEditorItem->qmlItemNode().anchors());
QRectF boundingRect(m_beginBoundingRect);
+
+ auto getRatioSizes = [&](){
+ double ratio = std::min(boundingRect.width() / m_beginBoundingRect.width(),
+ boundingRect.height() / m_beginBoundingRect.height());
+ double newW = m_beginBoundingRect.width() * ratio;
+ double newH = m_beginBoundingRect.height() * ratio;
+
+ return QSizeF(newW, newH);
+ };
+
if (m_resizeHandle->isBottomRightHandle()) {
boundingRect.setBottomRight(updatePointInLocalSpace);
@@ -132,6 +145,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
}
boundingRect.setBottomRight(updatePointInLocalSpace);
+ if (preserveAspectRatio) {
+ QSizeF newSize = getRatioSizes();
+
+ updatePointInLocalSpace.rx() = (boundingRect.topLeft().x() + newSize.width());
+ updatePointInLocalSpace.ry() = (boundingRect.topLeft().y() + newSize.height());
+
+ boundingRect.setBottomRight(updatePointInLocalSpace);
+ }
+
+ if (resizeFromCenter) {
+ QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(),
+ boundingRect.height() - m_beginBoundingRect.height() };
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineTop))
+ boundingRect.setTop(boundingRect.top() - grow.y());
+ if (!anchors.instanceHasAnchor(AnchorLineLeft))
+ boundingRect.setLeft(boundingRect.left() - grow.x());
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right()));
@@ -144,6 +179,7 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setHeight(minimumHeight);
formEditorItem->qmlItemNode().setSize(boundingRect.size());
+ formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft()));
if (anchors.instanceHasAnchor(AnchorLineBottom)) {
anchors.setMargin(AnchorLineBottom,
@@ -168,6 +204,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
}
boundingRect.setTopLeft(updatePointInLocalSpace);
+ if (preserveAspectRatio) {
+ QSizeF newSize = getRatioSizes();
+
+ updatePointInLocalSpace.rx() = (boundingRect.bottomRight().x() - newSize.width());
+ updatePointInLocalSpace.ry() = (boundingRect.bottomRight().y() - newSize.height());
+
+ boundingRect.setTopLeft(updatePointInLocalSpace);
+ }
+
+ if (resizeFromCenter) {
+ QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(),
+ boundingRect.height() - m_beginBoundingRect.height() };
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineBottom))
+ boundingRect.setBottom(boundingRect.bottom() + grow.y());
+ if (!anchors.instanceHasAnchor(AnchorLineRight))
+ boundingRect.setRight(boundingRect.right() + grow.x());
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left()));
@@ -208,6 +266,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
}
boundingRect.setTopRight(updatePointInLocalSpace);
+ if (preserveAspectRatio) {
+ QSizeF newSize = getRatioSizes();
+
+ updatePointInLocalSpace.rx() = (boundingRect.bottomLeft().x() + newSize.width());
+ updatePointInLocalSpace.ry() = (boundingRect.bottomLeft().y() - newSize.height());
+
+ boundingRect.setTopRight(updatePointInLocalSpace);
+ }
+
+ if (resizeFromCenter) {
+ QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(),
+ boundingRect.height() - m_beginBoundingRect.height() };
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineBottom))
+ boundingRect.setBottom(boundingRect.bottom() + grow.y());
+ if (!anchors.instanceHasAnchor(AnchorLineLeft))
+ boundingRect.setLeft(boundingRect.left() - grow.x());
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right()));
@@ -246,6 +326,28 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setBottomLeft(updatePointInLocalSpace);
+ if (preserveAspectRatio) {
+ QSizeF newSize = getRatioSizes();
+
+ updatePointInLocalSpace.rx() = (boundingRect.topRight().x() - newSize.width());
+ updatePointInLocalSpace.ry() = (boundingRect.topRight().y() + newSize.height());
+
+ boundingRect.setBottomLeft(updatePointInLocalSpace);
+ }
+
+ if (resizeFromCenter) {
+ QPointF grow = { boundingRect.width() - m_beginBoundingRect.width(),
+ boundingRect.height() - m_beginBoundingRect.height() };
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineTop))
+ boundingRect.setTop(boundingRect.top() - grow.y());
+ if (!anchors.instanceHasAnchor(AnchorLineRight))
+ boundingRect.setRight(boundingRect.right() + grow.x());
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left()));
@@ -280,13 +382,30 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setBottom(updatePointInLocalSpace.y());
+ if (resizeFromCenter) {
+ double grow = boundingRect.height() - m_beginBoundingRect.height();
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineTop))
+ boundingRect.setTop(boundingRect.top() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineLeft))
+ boundingRect.setLeft(boundingRect.left() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineRight))
+ boundingRect.setRight(boundingRect.right() + grow);
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineVerticalCenter))
boundingRect.setTop(boundingRect.top() - (updatePointInLocalSpace.y() - m_beginBoundingRect.bottom()));
+ if (boundingRect.width() < minimumWidth)
+ boundingRect.setWidth(minimumWidth);
if (boundingRect.height() < minimumHeight)
boundingRect.setHeight(minimumHeight);
formEditorItem->qmlItemNode().setSize(boundingRect.size());
+ formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft()));
if (anchors.instanceHasAnchor(AnchorLineBottom)) {
anchors.setMargin(AnchorLineBottom,
@@ -303,9 +422,25 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setTop(updatePointInLocalSpace.y());
+ if (resizeFromCenter) {
+ double grow = boundingRect.height() - m_beginBoundingRect.height();
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineBottom))
+ boundingRect.setBottom(boundingRect.bottom() + grow);
+ if (!anchors.instanceHasAnchor(AnchorLineLeft))
+ boundingRect.setLeft(boundingRect.left() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineRight))
+ boundingRect.setRight(boundingRect.right() + grow);
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineVerticalCenter))
boundingRect.setBottom(boundingRect.bottom() - (updatePointInLocalSpace.y() - m_beginBoundingRect.top()));
+ if (boundingRect.width() < minimumWidth)
+ boundingRect.setWidth(minimumWidth);
if (boundingRect.height() < minimumHeight)
boundingRect.setTop(boundingRect.top() - minimumHeight + boundingRect.height());
@@ -327,13 +462,30 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setRight(updatePointInLocalSpace.x());
+ if (resizeFromCenter) {
+ double grow = boundingRect.width() - m_beginBoundingRect.width();
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineTop))
+ boundingRect.setTop(boundingRect.top() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineLeft))
+ boundingRect.setLeft(boundingRect.left() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineBottom))
+ boundingRect.setBottom(boundingRect.bottom() + grow);
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setLeft(boundingRect.left() - (updatePointInLocalSpace.x() - m_beginBoundingRect.right()));
if (boundingRect.width() < minimumWidth)
boundingRect.setWidth(minimumWidth);
+ if (boundingRect.height() < minimumHeight)
+ boundingRect.setHeight(minimumHeight);
formEditorItem->qmlItemNode().setSize(boundingRect.size());
+ formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft()));
if (anchors.instanceHasAnchor(AnchorLineRight)) {
@@ -351,11 +503,27 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping use
boundingRect.setLeft(updatePointInLocalSpace.x());
+ if (resizeFromCenter) {
+ double grow = boundingRect.width() - m_beginBoundingRect.width();
+
+ if (!anchors.instanceHasAnchor(AnchorLineHorizontalCenter)
+ && !anchors.instanceHasAnchor(AnchorLineVerticalCenter)) {
+ if (!anchors.instanceHasAnchor(AnchorLineTop))
+ boundingRect.setTop(boundingRect.top() - grow);
+ if (!anchors.instanceHasAnchor(AnchorLineBottom))
+ boundingRect.setBottom(boundingRect.bottom() + grow);
+ if (!anchors.instanceHasAnchor(AnchorLineRight))
+ boundingRect.setRight(boundingRect.right() + grow);
+ }
+ }
+
if (anchors.instanceHasAnchor(AnchorLineHorizontalCenter))
boundingRect.setRight(boundingRect.right() - (updatePointInLocalSpace.x() - m_beginBoundingRect.left()));
if (boundingRect.width() < minimumWidth)
boundingRect.setLeft(boundingRect.left() - minimumWidth + boundingRect.width());
+ if (boundingRect.height() < minimumHeight)
+ boundingRect.setHeight(minimumHeight);
formEditorItem->qmlItemNode().setSize(boundingRect.size());
formEditorItem->qmlItemNode().setPosition(m_beginToParentTransform.map(boundingRect.topLeft()));
diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h
index ab4f97f9cb..551006d161 100644
--- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h
+++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h
@@ -46,7 +46,8 @@ public:
void removeHandle();
void begin(const QPointF& beginPoint);
- void update(const QPointF& updatePoint, Snapper::Snapping useSnapping);
+ void update(const QPointF& updatePoint, Snapper::Snapping useSnapping,
+ Qt::KeyboardModifiers keyMods = Qt::NoModifier);
void end(Snapper::Snapping useSnapping);
void moveBy(double deltaX, double deltaY);
diff --git a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp
index 8b4e5c552a..e959a4b6b1 100644
--- a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp
@@ -71,7 +71,8 @@ void ResizeTool::mouseMoveEvent(const QList<QGraphicsItem*> &,
QGraphicsSceneMouseEvent *event)
{
if (m_resizeManipulator.isActive())
- m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers()));
+ m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers()),
+ event->modifiers());
}
void ResizeTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp
new file mode 100644
index 0000000000..e8222e8ac8
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp
@@ -0,0 +1,438 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "transitiontool.h"
+
+#include <formeditorscene.h>
+#include <formeditorview.h>
+#include <formeditorwidget.h>
+#include <itemutilfunctions.h>
+#include <formeditoritem.h>
+#include <layeritem.h>
+
+#include <resizehandleitem.h>
+
+#include <bindingproperty.h>
+#include <nodeabstractproperty.h>
+#include <nodelistproperty.h>
+#include <nodemetainfo.h>
+#include <qmlitemnode.h>
+#include <qmldesignerplugin.h>
+#include <abstractaction.h>
+#include <designeractionmanager.h>
+#include <variantproperty.h>
+#include <rewritingexception.h>
+#include <rewritertransaction.h>
+
+#include <coreplugin/icore.h>
+#include <utils/qtcassert.h>
+
+#include <QApplication>
+#include <QGraphicsSceneMouseEvent>
+#include <QAction>
+#include <QMessageBox>
+#include <QPair>
+#include <QGraphicsSceneMouseEvent>
+
+namespace QmlDesigner {
+
+static bool isTransitionSource(const ModelNode &node)
+{
+ return QmlFlowTargetNode::isFlowEditorTarget(node);
+}
+
+static bool isTransitionTarget(const QmlItemNode &node)
+{
+ return QmlFlowTargetNode::isFlowEditorTarget(node)
+ && !node.isFlowActionArea()
+ && !node.isFlowWildcard();
+}
+
+class TransitionToolAction : public AbstractAction
+{
+public:
+ TransitionToolAction(const QString &name) : AbstractAction(name) {}
+
+ QByteArray category() const override
+ {
+ return QByteArray();
+ }
+
+ QByteArray menuId() const override
+ {
+ return "TransitionTool";
+ }
+
+ int priority() const override
+ {
+ return CustomActionsPriority;
+ }
+
+ Type type() const override
+ {
+ return ContextMenuAction;
+ }
+
+protected:
+ bool isVisible(const SelectionContext &selectionContext) const override
+ {
+ if (selectionContext.scenePosition().isNull())
+ return false;
+
+ if (selectionContext.singleNodeIsSelected())
+ return isTransitionSource(selectionContext.currentSingleSelectedNode());
+
+ return false;
+ }
+
+ bool isEnabled(const SelectionContext &selectionContext) const override
+ {
+ return isVisible(selectionContext);
+ }
+};
+
+class TransitionCustomAction : public TransitionToolAction
+{
+public:
+ TransitionCustomAction(const QString &name) : TransitionToolAction(name) {}
+
+ QByteArray category() const override
+ {
+ return ComponentCoreConstants::flowCategory;
+ }
+
+ SelectionContext selectionContext() const
+ {
+ return AbstractAction::selectionContext();
+ }
+
+};
+
+static QRectF paintedBoundingRect(FormEditorItem *item)
+{
+ QRectF boundingRect = item->qmlItemNode().instanceBoundingRect();
+ if (boundingRect.width() < 4)
+ boundingRect = item->boundingRect();
+ return boundingRect;
+}
+
+static QPointF centerPoint(FormEditorItem *item)
+{
+ QRectF boundingRect = paintedBoundingRect(item);
+ return QPointF(item->scenePos().x() + boundingRect.width() / 2,
+ item->scenePos().y() + boundingRect.height() / 2);
+}
+
+void static setToBoundingRect(QGraphicsRectItem *rect, FormEditorItem *item)
+{
+ QPolygonF boundingRectInSceneSpace(item->mapToScene(paintedBoundingRect(item)));
+ rect->setRect(boundingRectInSceneSpace.boundingRect());
+}
+
+TransitionTool::TransitionTool()
+ : QObject(), AbstractCustomTool()
+{
+
+ TransitionToolAction *transitionToolAction = new TransitionToolAction(tr("Add Transition"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(transitionToolAction);
+
+ connect(transitionToolAction->action(), &QAction::triggered,
+ this, &TransitionTool::activateTool);
+
+ TransitionCustomAction *removeAction = new TransitionCustomAction(tr("Remove Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAction);
+
+ connect(removeAction->action(), &QAction::triggered,
+ this, [removeAction](){
+
+ SelectionContext context = removeAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove Transitions", [&node](){
+ if (node.isValid())
+ node.removeTransitions();
+ });
+ });
+
+ TransitionCustomAction *removeAllTransitionsAction = new TransitionCustomAction(tr("Remove All Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAllTransitionsAction);
+
+ connect(removeAllTransitionsAction->action(), &QAction::triggered,
+ this, [removeAllTransitionsAction](){
+
+ if (QMessageBox::question(Core::ICore::dialogParent(),
+ tr("Remove All Transitions"),
+ tr("Do you really want to remove all transitions?"),
+ QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
+ return;
+
+ SelectionContext context = removeAllTransitionsAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove All Transitions", [&node](){
+ if (node.isValid() && node.flowView().isValid())
+ node.flowView().removeAllTransitions();
+ });
+ });
+
+ TransitionCustomAction *removeDanglingTransitionAction = new TransitionCustomAction(tr("Remove Dangling Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeDanglingTransitionAction);
+
+ connect(removeDanglingTransitionAction->action(), &QAction::triggered,
+ this, [removeDanglingTransitionAction](){
+
+ SelectionContext context = removeDanglingTransitionAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove Dangling Transitions", [&node](){
+ if (node.isValid() && node.flowView().isValid())
+ node.flowView().removeDanglingTransitions();
+ });
+ });
+}
+
+TransitionTool::~TransitionTool()
+{
+}
+
+void TransitionTool::clear()
+{
+ m_lineItem.reset(nullptr);
+ m_rectangleItem1.reset(nullptr);
+ m_rectangleItem2.reset(nullptr);
+
+ AbstractFormEditorTool::clear();
+}
+
+void TransitionTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ if (m_blockEvents)
+ return;
+
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ AbstractFormEditorTool::mousePressEvent(itemList, event);
+ TransitionTool::mouseMoveEvent(itemList, event);
+}
+
+void TransitionTool::mouseMoveEvent(const QList<QGraphicsItem*> & itemList,
+ QGraphicsSceneMouseEvent * event)
+{
+ if (!m_lineItem)
+ return;
+
+ QTC_ASSERT(currentFormEditorItem(), return);
+
+ const QPointF pos = centerPoint(m_formEditorItem);
+ lineItem()->setLine(pos.x(),
+ pos.y(),
+ event->scenePos().x(),
+ event->scenePos().y());
+
+ FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
+
+ if (formEditorItem
+ && formEditorItem->qmlItemNode().isValid()
+ && isTransitionTarget(formEditorItem->qmlItemNode().modelNode())) {
+ rectangleItem2()->setVisible(true);
+ setToBoundingRect(rectangleItem2(), formEditorItem);
+ } else {
+ rectangleItem2()->setVisible(false);
+ }
+}
+
+void TransitionTool::hoverMoveEvent(const QList<QGraphicsItem*> & itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ mouseMoveEvent(itemList, event);
+}
+
+void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/)
+{
+}
+
+void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/)
+{
+ view()->changeToSelectionTool();
+}
+
+void TransitionTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
+{
+}
+
+void TransitionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
+{
+}
+
+void TransitionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ if (m_blockEvents)
+ return;
+
+ if (event->button() == Qt::LeftButton) {
+ FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
+
+ if (formEditorItem
+ && QmlFlowTargetNode(formEditorItem->qmlItemNode().modelNode()).isValid())
+ createTransition(m_formEditorItem, formEditorItem);
+ }
+
+ view()->changeToSelectionTool();
+}
+
+
+void TransitionTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
+{
+ AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event);
+}
+
+void TransitionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &)
+{
+ view()->changeCurrentToolTo(this);
+}
+
+void TransitionTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
+{
+ if (!itemList.isEmpty()) {
+ createItems();
+
+ m_formEditorItem = itemList.first();
+ setToBoundingRect(rectangleItem1(), m_formEditorItem);
+ }
+}
+
+void TransitionTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
+{
+}
+
+void TransitionTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
+{
+}
+
+void TransitionTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/)
+{
+}
+
+void TransitionTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
+{
+}
+
+int TransitionTool::wantHandleItem(const ModelNode &modelNode) const
+{
+ if (isTransitionSource(modelNode))
+ return 10;
+
+ return 0;
+}
+
+QString TransitionTool::name() const
+{
+ return tr("Transition Tool");
+}
+
+void TransitionTool::activateTool()
+{
+ view()->changeToCustomTool();
+}
+
+void TransitionTool::unblock()
+{
+ m_blockEvents = false;
+}
+
+QGraphicsLineItem *TransitionTool::lineItem()
+{
+ return m_lineItem.get();
+}
+
+QGraphicsRectItem *TransitionTool::rectangleItem1()
+{
+ return m_rectangleItem1.get();
+}
+
+QGraphicsRectItem *TransitionTool::rectangleItem2()
+{
+ return m_rectangleItem2.get();
+}
+
+FormEditorItem *TransitionTool::currentFormEditorItem() const
+{
+ if (scene()->items().contains(m_formEditorItem))
+ return m_formEditorItem;
+
+ return nullptr;
+}
+
+void TransitionTool::createItems() {
+ m_blockEvents = true;
+ QTimer::singleShot(200, this, [this](){ unblock(); });
+
+ if (!lineItem())
+ m_lineItem.reset(new QGraphicsLineItem(scene()->manipulatorLayerItem()));
+
+ if (!rectangleItem1())
+ m_rectangleItem1.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
+
+ if (!rectangleItem2())
+ m_rectangleItem2.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
+
+ m_rectangleItem2->setVisible(false);
+
+ QPen pen;
+ pen.setColor(QColor(Qt::lightGray));
+ pen.setStyle(Qt::DashLine);
+ pen.setWidth(0);
+ m_lineItem->setPen(pen);
+
+ pen.setColor(QColor(108, 141, 221));
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidth(4);
+ pen.setCosmetic(true);
+ m_rectangleItem1->setPen(pen);
+
+ m_rectangleItem2->setPen(pen);
+}
+
+void TransitionTool::createTransition(FormEditorItem *source, FormEditorItem *target)
+{
+ QmlFlowTargetNode sourceNode(source->qmlItemNode().modelNode());
+ QmlFlowTargetNode targetNode(target->qmlItemNode().modelNode());
+
+ if (sourceNode.isValid() && targetNode.isValid()
+ && sourceNode != targetNode
+ && !targetNode.isFlowActionArea()
+ && !targetNode.isFlowWildcard()) {
+ view()->executeInTransaction("create transition", [&sourceNode, targetNode](){
+ sourceNode.assignTargetItem(targetNode);
+ });
+ } else {
+ qWarning() << Q_FUNC_INFO << "nodes invalid";
+ }
+}
+
+}
diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.h b/src/plugins/qmldesigner/components/formeditor/transitiontool.h
new file mode 100644
index 0000000000..43c3894933
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#pragma once
+
+#include "abstractcustomtool.h"
+#include "selectionindicator.h"
+
+#include <QGraphicsLineItem>
+#include <QHash>
+#include <QPointer>
+
+#include <memory>
+
+namespace QmlDesigner {
+
+class TransitionTool : public QObject, public AbstractCustomTool
+{
+ Q_OBJECT
+public:
+ TransitionTool();
+ ~TransitionTool();
+
+ 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;
+
+ void activateTool();
+ void unblock();
+
+ QGraphicsLineItem *lineItem();
+ QGraphicsRectItem *rectangleItem1();
+ QGraphicsRectItem *rectangleItem2();
+
+private:
+ FormEditorItem *currentFormEditorItem() const;
+ void createItems();
+ void createTransition(FormEditorItem *item1, FormEditorItem *item2);
+
+ FormEditorItem* m_formEditorItem;
+ std::unique_ptr<QGraphicsLineItem> m_lineItem;
+ std::unique_ptr<QGraphicsRectItem> m_rectangleItem1;
+ std::unique_ptr<QGraphicsRectItem> m_rectangleItem2;
+ bool m_blockEvents = true;
+};
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp
index df9590af64..c2bce0b0ce 100644
--- a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp
+++ b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp
@@ -81,14 +81,16 @@ void ImportManagerView::modelAboutToBeDetached(Model *model)
void ImportManagerView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
{
- if (m_importsWidget)
+ if (m_importsWidget) {
m_importsWidget->setImports(model()->imports());
+ // setImports recreates labels, so we need to update used imports, as it is not guaranteed
+ // usedImportsChanged notification will come after this.
+ m_importsWidget->setUsedImports(model()->usedImports());
+ }
}
void ImportManagerView::possibleImportsChanged(const QList<Import> &/*possibleImports*/)
{
- QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
-
if (m_importsWidget)
m_importsWidget->setPossibleImports(model()->possibleImports());
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 3f403d0313..0beb5bce72 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -32,6 +32,9 @@
#include "utils/outputformatter.h"
#include "theme.h"
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
@@ -97,6 +100,20 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
+ QStringList importPaths;
+ auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
+ if (doc) {
+ Model *model = doc->currentModel();
+ if (model)
+ importPaths = model->importPaths();
+ }
+
+ QString targetDir = defaulTargetDirectory;
+
+ ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(doc->fileName());
+ if (currentProject)
+ targetDir = currentProject->projectDirectory().toString();
+
// Import is always done under known folder. The order of preference for folder is:
// 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
// 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path
@@ -105,19 +122,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
// 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project
const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER);
const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
- QString candidatePath = defaulTargetDirectory + defaultAssetFolder + quick3DFolder;
+ QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder;
int candidatePriority = 5;
- QStringList importPaths;
-
- auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
- if (doc) {
- Model *model = doc->currentModel();
- if (model)
- importPaths = model->importPaths();
- }
for (auto importPath : qAsConst(importPaths)) {
- if (importPath.startsWith(defaulTargetDirectory)) {
+ if (importPath.startsWith(targetDir)) {
const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder);
const QString assetFolder = importPath + quick3DFolder;
const bool exists = QFileInfo(assetFolder).exists();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 34fa71222d..6a5edb18a3 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -280,7 +280,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
return;
}
+ QString originalAssetName = assetName;
if (targetDir.exists(assetName)) {
+ // If we have a file system with case insensitive filenames, assetName may be
+ // different from the existing name. Modify assetName to ensure exact match to
+ // the overwritten old asset capitalization
+ const QStringList assetDirs = targetDir.entryList({assetName}, QDir::Dirs);
+ if (assetDirs.size() == 1) {
+ assetName = assetDirs[0];
+ targetDirPath = targetDir.filePath(assetName);
+ }
if (!confirmAssetOverwrite(assetName)) {
addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName));
return;
@@ -306,6 +315,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
return;
}
+ // The importer is reset after every import to avoid issues with it caching various things
+ m_quick3DAssetImporter.reset(new QSSGAssetImportManager);
+
+ if (originalAssetName != assetName) {
+ // Fix the generated qml file name
+ const QString assetQml = originalAssetName + ".qml";
+ if (outDir.exists(assetQml))
+ outDir.rename(assetQml, assetName + ".qml");
+ }
+
QHash<QString, QString> assetFiles;
const int outDirPathSize = outDir.path().size();
auto insertAsset = [&](const QString &filePath) {
@@ -512,18 +531,24 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
addInfo(progressTitle);
notifyProgress(0, progressTitle);
- // There is an inbuilt delay before rewriter change actually updates the data model,
- // so we need to wait for a moment to allow the change to take effect.
+ // First we have to wait a while to ensure qmljs detects new files and updates its
+ // internal model. Then we make a non-change to the document to trigger qmljs snapshot
+ // update. There is an inbuilt delay before rewriter change actually updates the data
+ // model, so we need to wait for another moment to allow the change to take effect.
// Otherwise subsequent subcomponent manager update won't detect new imports properly.
QTimer *timer = new QTimer(parent());
static int counter;
counter = 0;
- timer->callOnTimeout([this, timer, progressTitle, model]() {
+ timer->callOnTimeout([this, timer, progressTitle, model, doc]() {
if (!isCancelled()) {
- notifyProgress(++counter * 10, progressTitle);
- if (counter >= 10) {
- // Trigger underlying qmljs snapshot update by making a non-change to the doc
+ notifyProgress(++counter * 5, progressTitle);
+ if (counter == 10) {
model->rewriterView()->textModifier()->replace(0, 0, {});
+ } else if (counter == 19) {
+ doc->updateSubcomponentManager();
+ } else if (counter >= 20) {
+ if (!m_overwrittenImports.isEmpty())
+ model->rewriterView()->emitCustomNotification("asset_import_update");
timer->stop();
notifyFinished();
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index caf0663dce..3f7ca2c366 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -384,7 +384,19 @@ void ItemLibraryWidget::updateModel()
{
QTC_ASSERT(m_itemLibraryModel, return);
+ if (m_compressionTimer.isActive()) {
+ m_updateRetry = false;
+ m_compressionTimer.stop();
+ }
+
m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data());
+
+ if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) {
+ m_updateRetry = true; // Only retry once to avoid endless loops
+ m_compressionTimer.start();
+ } else {
+ m_updateRetry = false;
+ }
updateImports();
updateSearch();
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index 6e8fc0340a..11dea7d0c1 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -121,6 +121,7 @@ private:
QPointer<Model> m_model;
FilterChangeFlag m_filterFlag;
ItemLibraryEntry m_currentitemLibraryEntry;
+ bool m_updateRetry = false;
};
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h
index bedcc2f561..d560eb2824 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h
@@ -43,6 +43,7 @@ public:
virtual void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) = 0;
virtual void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) = 0;
virtual void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) = 0;
+ virtual void notifyIconsChanged() = 0;
virtual void setFilter(bool showObjects) = 0;
virtual void resetModel() = 0;
};
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index c91ff78626..70d2e405f9 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -695,6 +695,11 @@ void NavigatorTreeModel::notifyModelNodesMoved(const QList<ModelNode> &modelNode
emit layoutChanged(indexes);
}
+void NavigatorTreeModel::notifyIconsChanged()
+{
+ emit dataChanged(index(0, 0), index(rowCount(), 0), {Qt::DecorationRole});
+}
+
void NavigatorTreeModel::setFilter(bool showOnlyVisibleItems)
{
m_showOnlyVisibleItems = showOnlyVisibleItems;
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
index f10198adcc..15e89d3636 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
@@ -87,6 +87,7 @@ public:
void notifyModelNodesRemoved(const QList<ModelNode> &modelNodes) override;
void notifyModelNodesInserted(const QList<ModelNode> &modelNodes) override;
void notifyModelNodesMoved(const QList<ModelNode> &modelNodes) override;
+ void notifyIconsChanged() override;
void setFilter(bool showOnlyVisibleItems) override;
void resetModel() override;
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
index ea64f0715a..cd6857d789 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
@@ -147,6 +147,17 @@ void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & prop
}
}
+void NavigatorView::customNotification(const AbstractView *view, const QString &identifier,
+ const QList<ModelNode> &nodeList, const QList<QVariant> &data)
+{
+ Q_UNUSED(view)
+ Q_UNUSED(nodeList)
+ Q_UNUSED(data)
+
+ if (identifier == "asset_import_update")
+ m_currentModelInterface->notifyIconsChanged();
+}
+
void NavigatorView::handleChangedExport(const ModelNode &modelNode, bool exported)
{
const ModelNode rootNode = rootModelNode();
@@ -416,12 +427,24 @@ void NavigatorView::updateItemSelection()
QItemSelection itemSelection;
foreach (const ModelNode &node, selectedModelNodes()) {
const QModelIndex index = indexForModelNode(node);
+
if (index.isValid()) {
const QModelIndex beginIndex(currentModel()->index(index.row(), 0, index.parent()));
const QModelIndex endIndex(currentModel()->index(index.row(), currentModel()->columnCount(index.parent()) - 1, index.parent()));
if (beginIndex.isValid() && endIndex.isValid())
itemSelection.select(beginIndex, endIndex);
- }
+ } else {
+ // if the node index is invalid expand ancestors manually if they are valid.
+ ModelNode parentNode = node;
+ while (parentNode.hasParentProperty()) {
+ parentNode = parentNode.parentProperty().parentQmlObjectNode();
+ QModelIndex parentIndex = indexForModelNode(parentNode);
+ if (parentIndex.isValid())
+ treeWidget()->expand(parentIndex);
+ else
+ break;
+ }
+ }
}
bool blocked = blockSelectionChangedSignal(true);
@@ -431,10 +454,10 @@ void NavigatorView::updateItemSelection()
if (!selectedModelNodes().isEmpty())
treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constFirst()));
- // make sure selected nodes a visible
+ // make sure selected nodes are visible
foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) {
if (selectedIndex.column() == 0)
- expandRecursively(selectedIndex);
+ expandAncestors(selectedIndex);
}
}
@@ -458,9 +481,9 @@ bool NavigatorView::blockSelectionChangedSignal(bool block)
return oldValue;
}
-void NavigatorView::expandRecursively(const QModelIndex &index)
+void NavigatorView::expandAncestors(const QModelIndex &index)
{
- QModelIndex currentIndex = index;
+ QModelIndex currentIndex = index.parent();
while (currentIndex.isValid()) {
if (!treeWidget()->isExpanded(currentIndex))
treeWidget()->expand(currentIndex);
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h
index 852dddc70f..3bafe0fa80 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h
@@ -84,6 +84,8 @@ public:
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags) override;
+ void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
+
void handleChangedExport(const ModelNode &modelNode, bool exported);
bool isNodeInvisible(const ModelNode &modelNode) const;
@@ -108,7 +110,7 @@ protected: //functions
QTreeView *treeWidget() const;
NavigatorTreeModel *treeModel();
bool blockSelectionChangedSignal(bool block);
- void expandRecursively(const QModelIndex &index);
+ void expandAncestors(const QModelIndex &index);
void reparentAndCatch(NodeAbstractProperty property, const ModelNode &modelNode);
void setupWidget();
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
index 8be49060e6..1a52a639a7 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
@@ -426,6 +426,17 @@ QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &fami
return dataBase.styles(family);
}
+QStringList PropertyEditorContextObject::allStatesForId(const QString &id)
+{
+ if (m_model && m_model->rewriterView()) {
+ const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id);
+ if (node.isValid())
+ return node.allStateNames();
+ }
+
+ return {};
+}
+
void EasingCurveEditor::registerDeclarativeType()
{
qmlRegisterType<EasingCurveEditor>("HelperWidgets", 2, 0, "EasingCurveEditor");
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
index 9e3309ee35..03d82dbc34 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h
@@ -94,6 +94,8 @@ public:
Q_INVOKABLE QStringList styleNamesForFamily(const QString &family);
+ Q_INVOKABLE QStringList allStatesForId(const QString &id);
+
int majorVersion() const;
int majorQtQuickVersion() const;
int minorQtQuickVersion() const;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index ddded6400b..6dbb0d471c 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -47,6 +47,7 @@
#include <QApplication>
#include <QDir>
#include <QFileInfo>
+#include <QVector3D>
#include <QLoggingCategory>
@@ -166,6 +167,22 @@ QVariant properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
return 0;
else if (propertyName == "breakPoint")
return 50;
+ else if (propertyName == "transitionType")
+ return 0;
+ else if (propertyName == "type")
+ return 0;
+ else if (propertyName == "transitionRadius")
+ return 8;
+ else if (propertyName == "radius")
+ return 8;
+ else if (propertyName == "transitionBezier")
+ return 50;
+ else if (propertyName == "bezier")
+ return 50;
+ else if (propertyName == "labelPosition")
+ return 50.0;
+ else if (propertyName == "labelFlipSide")
+ return false;
else if (propertyName == "customId")
return QString();
else if (propertyName == "joinConnection")
@@ -235,7 +252,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
propertyNames.append("customId");
if (itemNode.isFlowTransition()) {
- propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint"});
+ propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "breakPoint", "type", "radius", "bezier", "labelPosition", "labelFlipSide"});
} else if (itemNode.isFlowItem()) {
propertyNames.append({"color", "width", "inOffset", "outOffset", "joinConnection"});
} else if (itemNode.isFlowActionArea()) {
@@ -245,7 +262,7 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
} else if (itemNode.isFlowWildcard()) {
propertyNames.append({"color", "width", "fillColor", "dash"});
} else if (itemNode.isFlowView()) {
- propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor" });
+ propertyNames.append({"transitionColor", "areaColor", "areaFillColor", "blockColor", "transitionType", "transitionRadius", "transitionBezier"});
}
for (const PropertyName &propertyName : propertyNames) {
@@ -294,11 +311,25 @@ void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qm
void PropertyEditorQmlBackend::setValue(const QmlObjectNode & , const PropertyName &name, const QVariant &value)
{
- PropertyName propertyName = name;
- propertyName.replace('.', '_');
- auto propertyValue = qobject_cast<PropertyEditorValue*>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(propertyName))));
- if (propertyValue)
- propertyValue->setValue(value);
+ if (value.type() == QVariant::Vector3D) {
+ // Vector3D values need to be split into their subcomponents
+ const char *suffix[3] = {"_x", "_y", "_z"};
+ auto vecValue = value.value<QVector3D>();
+ for (int i = 0; i < 3; ++i) {
+ PropertyName subPropName(name.size() + 2, '\0');
+ subPropName.replace(0, name.size(), name);
+ subPropName.replace(name.size(), 2, suffix[i]);
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(subPropName))));
+ if (propertyValue)
+ propertyValue->setValue(QVariant(vecValue[i]));
+ }
+ } else {
+ PropertyName propertyName = name;
+ propertyName.replace('.', '_');
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(variantToQObject(m_backendValuesPropertyMap.value(QString::fromUtf8(propertyName))));
+ if (propertyValue)
+ propertyValue->setValue(value);
+ }
}
QQmlContext *PropertyEditorQmlBackend::context() {