diff options
author | Eike Ziller <eike.ziller@qt.io> | 2022-09-27 10:03:37 +0200 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2022-09-27 10:03:37 +0200 |
commit | 7eaa36e6e58a7c4ff8ec8a691c2a5abc39ae5f30 (patch) | |
tree | 872b997978f8065ec1d58df52301d9e1b308683c | |
parent | 21c5d3499ba143f4ff703410174e27c11f7ddda3 (diff) | |
parent | 4da66867051b27354b71ff6b4690d4e2d1e53bd6 (diff) | |
download | qt-creator-7eaa36e6e58a7c4ff8ec8a691c2a5abc39ae5f30.tar.gz |
Merge remote-tracking branch 'origin/8.0' into 9.0
Conflicts:
src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp
Change-Id: I38f196e8f42cf11f7b613e7a723745600e35c5e9
61 files changed, 932 insertions, 201 deletions
diff --git a/doc/qtdesignstudio/images/3d-view-context-menu.png b/doc/qtdesignstudio/images/3d-view-context-menu.png Binary files differnew file mode 100644 index 0000000000..c2e35e0019 --- /dev/null +++ b/doc/qtdesignstudio/images/3d-view-context-menu.png diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc index 303a713a63..202249188d 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc @@ -16,10 +16,18 @@ \image studio-qtquick-3d-components.png "Qt Quick 3D components in Components" - Add a camera by dragging-and-dropping one of the camera components from - \uicontrol Components > \uicontrol {Qt Quick 3D} > \uicontrol - {Qt Quick 3D} to the \l {3D} view or to a 3D view in \l Navigator. - If the cameras are not displayed in \uicontrol {Components}, add the + To add a camera component to your UI, do one of the following: + \list + \li Drag a camera component from \uicontrol Components > + \uicontrol {Qt Quick 3D} to the \l {3D} view or to + \l Navigator > \uicontrol {View3D} > \uicontrol Scene. + \li Right-click in the \uicontrol 3D view and select + \uicontrol Create > \uicontrol Cameras from the context menu. + \note You can only create \uicontrol {Camera Perspective} and + \uicontrol {Camera Ortographic} this way. + \endlist + + If you cannot find the camera components in \uicontrol {Components}, add the \uicontrol QtQuick3D module to your project, as described in \l {Adding and Removing Modules}. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index e7487a7709..810692e89b 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -48,6 +48,16 @@ Additionally, you can toggle the visibility of the grid, selection boxes, icon gizmos, and camera frustums in the 3D scene. + There is a context menu in the \uicontrol 3D view. To open it, right-click + in the \uicontrol 3D view. From the context menu you can: + \list + \li Create cameras, lights, and models. + \li Open \uicontrol {Material Editor} and edit materials. + \li Delete components + \endlist + + \image 3d-view-context-menu.png + To refresh the contents of the \uicontrol{3D} view, press \key P or select the \inlineimage icons/reset.png (\uicontrol {Reset View}) button. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc index 4ce143c8ce..4bd44874ab 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc @@ -12,9 +12,14 @@ As a secondary light source, you can use \l{Setting the Light Probe} {image-based lighting}. - To add light components to your UI, drag-and-drop them from - \uicontrol Components > \uicontrol {Qt Quick 3D} to the \l {3D} view or to - \l Navigator > \uicontrol {Scene Environment} > \uicontrol Scene. + To add a light component to your UI, do one of the following: + \list + \li Drag a light component from \uicontrol Components > + \uicontrol {Qt Quick 3D} to the \l {3D} view or to + \l Navigator > \uicontrol {View3D} > \uicontrol Scene. + \li Right-click in the \uicontrol 3D view and select + \uicontrol Create > \uicontrol Lights from the context menu. + \endlist If you cannot find the light components in \uicontrol {Components}, add the \uicontrol {Qt Quick 3D} module to your project as instructed in diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc index 629a628490..9070ef0835 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc @@ -13,14 +13,21 @@ \image studio-3d-models.png "Various 3D models in the 3D view" - A Model component loads mesh data from a file. You can modify how the + A model component loads mesh data from a file. You can modify how the component is shaded by using materials. For more information, see \l {Materials and Shaders} and \l {Creating Custom Materials}. - You can drag-and-drop a model from \uicontrol Components - > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D} to the \l {3D} view or - to \l Navigator > \uicontrol {Scene Environment} > \uicontrol Scene. If the - models are not displayed in \uicontrol {Components}, you should add the + To add a model component to your UI, do one of the following: + \list + \li Drag a model component from \uicontrol Components > + \uicontrol {Qt Quick 3D} to the \l {3D} view or to + \l Navigator > \uicontrol {View3D} > \uicontrol Scene. + \li Right-click in the \uicontrol 3D view and select + \uicontrol Create > \uicontrol Primitives from the context menu. + \note You can not create \uicontrol Empty models this way. + \endlist + + If you cannot find the model components in \uicontrol {Components}, add the \uicontrol QtQuick3D module to your project, as described in \l {Adding and Removing Modules}. diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index d1b7beda4e..9fe970f8c7 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -511,7 +511,7 @@ void NodeInstanceServer::setupOnlyWorkingImports(const QStringList &workingImpor quickView()->setContent(fileUrl(), m_importComponent, quickView()->rootObject()); m_importComponent->setData(componentCode.append("\nItem {}\n"), fileUrl()); - m_importComponentObject = m_importComponent->create(); + m_importComponentObject = m_importComponent->create(engine()->rootContext()); Q_ASSERT(m_importComponent && m_importComponentObject); Q_ASSERT_X(m_importComponent->errors().isEmpty(), __FUNCTION__, m_importComponent->errorString().toLatin1()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp index f2f869de19..e60ca48da7 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.cpp @@ -94,5 +94,21 @@ bool QmlStateNodeInstance::resetStateProperty(const ObjectNodeInstance::Pointer return QmlPrivateGate::States::resetStateProperty(object(), target->object(), propertyName, resetValue); } +void QmlStateNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParentInstance, + const PropertyName &oldParentProperty, + const ObjectNodeInstance::Pointer &newParentInstance, + const PropertyName &newParentProperty) +{ + ServerNodeInstance oldState = nodeInstanceServer()->activeStateInstance(); + + ObjectNodeInstance::reparent(oldParentInstance, + oldParentProperty, + newParentInstance, + newParentProperty); + + if (oldState.isValid()) + oldState.activateState(); +} + } // namespace Internal } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.h index 0564ffbc40..95fce95c1b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qmlstatenodeinstance.h @@ -27,6 +27,10 @@ public: bool updateStateBinding(const ObjectNodeInstance::Pointer &target, const PropertyName &propertyName, const QString &expression) override; bool resetStateProperty(const ObjectNodeInstance::Pointer &target, const PropertyName &propertyName, const QVariant &resetValue) override; + void reparent(const ObjectNodeInstance::Pointer &oldParentInstance, + const PropertyName &oldParentProperty, + const ObjectNodeInstance::Pointer &newParentInstance, + const PropertyName &newParentProperty) override; protected: QmlStateNodeInstance(QObject *object); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.cpp index 0ed2d71028..d518fa6ae5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.cpp @@ -15,12 +15,13 @@ Quick3DMaterialNodeInstance::~Quick3DMaterialNodeInstance() { } -void Quick3DMaterialNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, - InstanceContainer::NodeFlags flags) +void Quick3DMaterialNodeInstance::invokeDummyViewCreate() const { - m_dummyRootViewCreateFunction = "createViewForMaterial"; - - Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags); + QMetaObject::invokeMethod(m_dummyRootView, "createViewForMaterial", + Q_ARG(QVariant, QVariant::fromValue(object())), + Q_ARG(QVariant, ""), + Q_ARG(QVariant, ""), + Q_ARG(QVariant, "")); } Quick3DMaterialNodeInstance::Pointer Quick3DMaterialNodeInstance::create(QObject *object) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.h index 8a3fd97237..84edfc2ea0 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dmaterialnodeinstance.h @@ -19,11 +19,10 @@ public: ~Quick3DMaterialNodeInstance() override; static Pointer create(QObject *objectToBeWrapped); - void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, - InstanceContainer::NodeFlags flags) override; protected: explicit Quick3DMaterialNodeInstance(QObject *node); + void invokeDummyViewCreate() const override; }; } // namespace Internal diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp index 4755c8d416..95e4a8281b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -24,6 +24,12 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node) { } +void Quick3DNodeInstance::invokeDummyViewCreate() const +{ + QMetaObject::invokeMethod(m_dummyRootView, "createViewForNode", + Q_ARG(QVariant, QVariant::fromValue(object()))); +} + Quick3DNodeInstance::~Quick3DNodeInstance() { } @@ -58,8 +64,6 @@ void Quick3DNodeInstance::initialize( } } - m_dummyRootViewCreateFunction = "createViewForNode"; - Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags); #endif // QUICK3D_MODULE } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h index 2c1f6372bc..b8120a9092 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h @@ -25,6 +25,7 @@ public: protected: explicit Quick3DNodeInstance(QObject *node); + void invokeDummyViewCreate() const override; private: QQuick3DNode *quick3DNode() const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp index ce6e94b4ae..67741db5de 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp @@ -28,7 +28,7 @@ Quick3DRenderableNodeInstance::~Quick3DRenderableNodeInstance() } void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, - InstanceContainer::NodeFlags flags) + InstanceContainer::NodeFlags flags) { #ifdef QUICK3D_MODULE #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -45,8 +45,7 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml")); m_dummyRootView = qobject_cast<QQuickItem *>(component.create()); - QMetaObject::invokeMethod(m_dummyRootView, m_dummyRootViewCreateFunction, - Q_ARG(QVariant, QVariant::fromValue(object()))); + invokeDummyViewCreate(); nodeInstanceServer()->setRootItem(m_dummyRootView); } @@ -192,6 +191,10 @@ Qt5NodeInstanceServer *Quick3DRenderableNodeInstance::qt5NodeInstanceServer() co return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer()); } +void Quick3DRenderableNodeInstance::invokeDummyViewCreate() const +{ +} + } // namespace Internal } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h index 8c82eed987..98fba654d8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h @@ -35,10 +35,8 @@ public: protected: explicit Quick3DRenderableNodeInstance(QObject *node); Qt5NodeInstanceServer *qt5NodeInstanceServer() const; + virtual void invokeDummyViewCreate() const; - QByteArray m_dummyRootViewCreateFunction; - -private: QQuickItem *m_dummyRootView = nullptr; }; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp index 2e6ccead54..918e147798 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -846,6 +846,10 @@ void QuickItemNodeInstance::setPropertyVariant(const PropertyName &name, const Q void QuickItemNodeInstance::setPropertyBinding(const PropertyName &name, const QString &expression) { + static QList<PropertyName> anchorsTargets = {"anchors.top", + "acnhors.bottom", + "anchors.left", + "achors.right"}; if (ignoredProperties().contains(name)) return; @@ -857,7 +861,15 @@ void QuickItemNodeInstance::setPropertyBinding(const PropertyName &name, const Q markRepeaterParentDirty(); - ObjectNodeInstance::setPropertyBinding(name, expression); + if (anchorsTargets.contains(name)) { + //When resolving anchor targets we have to provide the root context the ids are defined in. + QmlPrivateGate::setPropertyBinding(object(), + context()->engine()->rootContext(), + name, + expression); + } else { + ObjectNodeInstance::setPropertyBinding(name, expression); + } refresh(); diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index afe81c3b8f..1b9a284ce1 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -414,10 +414,12 @@ Item { width: parent.width - SearchBox { + StudioControls.SearchBox { id: searchBox width: parent.width - addAssetButton.width - 5 + + onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText) } IconButton { diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index c62a4cc82e..c523fb7b70 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -199,10 +199,12 @@ Item { Row { width: parent.width - SearchBox { + StudioControls.SearchBox { id: searchBox width: parent.width - addModuleButton.width - 5 + + onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText) } IconButton { diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index bda29dcb13..ba72ef03a8 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -208,10 +208,12 @@ Item { width: root.width enabled: !materialBrowserModel.hasMaterialRoot && materialBrowserModel.hasQuick3DImport - SearchBox { + StudioControls.SearchBox { id: searchBox width: root.width - addMaterialButton.width + + onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText) } IconButton { diff --git a/share/qtcreator/qmldesigner/newstateseditor/Main.qml b/share/qtcreator/qmldesigner/newstateseditor/Main.qml index 8f50d8f336..29feb5741d 100644 --- a/share/qtcreator/qmldesigner/newstateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/newstateseditor/Main.qml @@ -26,6 +26,7 @@ import QtQuick import QtQuick.Controls import StatesEditor +import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -369,6 +370,23 @@ Rectangle { width: stateGroupLabel.visible ? StudioTheme.Values.defaultControlWidth : root.width - 2 * root.padding + HelperWidgets.Tooltip { id: comboBoxTooltip } + + Timer { + interval: 1000 + running: stateGroupComboBox.hovered + onTriggered: comboBoxTooltip.showText(stateGroupComboBox, + hoverHandler.point.position, + qsTr("Switch State Group")) + } + + onHoverChanged: { + if (!stateGroupComboBox.hovered) + comboBoxTooltip.hideText() + } + + HoverHandler { id: hoverHandler } + popup.onOpened: editDialog.close() // currentIndex needs special treatment, because if model is changed, it will be @@ -398,25 +416,28 @@ Rectangle { spacing: StudioTheme.Values.toolbarSpacing leftPadding: toolBar.doubleRow ? root.padding : 0 - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { buttonIcon: StudioTheme.Constants.plus anchors.verticalCenter: parent.verticalCenter + tooltip: qsTr("Create State Group") onClicked: statesEditorModel.addStateGroup("stateGroup") } - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { buttonIcon: StudioTheme.Constants.minus anchors.verticalCenter: parent.verticalCenter enabled: statesEditorModel.activeStateGroupIndex !== 0 + tooltip: qsTr("Remove State Group") onClicked: statesEditorModel.removeStateGroup() } - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { id: editButton buttonIcon: StudioTheme.Constants.edit anchors.verticalCenter: parent.verticalCenter enabled: statesEditorModel.activeStateGroupIndex !== 0 checked: editDialog.visible + tooltip: qsTr("Rename State Group") onClicked: { if (editDialog.opened) editDialog.close() @@ -439,20 +460,22 @@ Rectangle { spacing: StudioTheme.Values.toolbarSpacing rightPadding: root.padding - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { buttonIcon: StudioTheme.Constants.gridView anchors.verticalCenter: parent.verticalCenter enabled: !root.tinyMode + tooltip: qsTr("Show thumbnails") onClicked: { for (var i = 0; i < statesRepeater.count; ++i) statesRepeater.itemAt(i).setPropertyChangesVisible(false) } } - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { buttonIcon: StudioTheme.Constants.textFullJustification anchors.verticalCenter: parent.verticalCenter enabled: !root.tinyMode + tooltip: qsTr("Show property changes") onClicked: { for (var i = 0; i < statesRepeater.count; ++i) statesRepeater.itemAt(i).setPropertyChangesVisible(true) @@ -503,6 +526,7 @@ Rectangle { anchors.leftMargin: root.leftMargin ScrollBar.horizontal: StateScrollBar { + id: horizontalBar parent: scrollView x: scrollView.leftPadding y: scrollView.height - height @@ -511,6 +535,7 @@ Rectangle { } ScrollBar.vertical: StateScrollBar { + id: verticalBar parent: scrollView x: scrollView.mirrored ? 0 : scrollView.width - width y: scrollView.topPadding @@ -568,6 +593,7 @@ Rectangle { NumberAnimation { properties: "x,y" easing.type: Easing.OutQuad + duration: 100 } } @@ -576,11 +602,6 @@ Rectangle { property int grabIndex: -1 - function executeDrop(from, to) { - statesEditorModel.drop(from, to) - statesRepeater.grabIndex = -1 - } - model: statesEditorModel onItemAdded: root.responsiveResize(root.width, root.height) @@ -623,24 +644,27 @@ Rectangle { return } - statesEditorModel.move( - (drag.source as StateThumbnail).visualIndex, - stateThumbnail.visualIndex) + statesEditorModel.move(dragSource.visualIndex, + stateThumbnail.visualIndex) } onDropped: function (drop) { - let dragSource = (drop.source as StateThumbnail) + let dropSource = (drop.source as StateThumbnail) - if (dragSource === undefined) + if (dropSource === undefined) return - if (dragSource.extendString !== stateThumbnail.extendString + if (dropSource.extendString !== stateThumbnail.extendString || stateThumbnail.extendedState) { return } - statesRepeater.executeDrop(statesRepeater.grabIndex, - stateThumbnail.visualIndex) + if (statesRepeater.grabIndex === dropSource.visualIndex) + return + + statesEditorModel.drop(statesRepeater.grabIndex, + dropSource.visualIndex) + statesRepeater.grabIndex = -1 } // Extend Groups Visualization @@ -742,6 +766,10 @@ Rectangle { hasWhenCondition: delegateRoot.hasWhenCondition + scrollViewActive: horizontalBar.active || verticalBar.active + + dragParent: scrollView + // Fix ScrollView taking over the dragging event onGrabbing: { frame.interactive = false @@ -749,14 +777,6 @@ Rectangle { } onLetGo: frame.interactive = true - // Fix for ScrollView clipping while dragging of StateThumbnail - onDragActiveChanged: { - if (stateThumbnail.dragActive) - parent = scrollViewWrapper - else - parent = delegateRoot - } - stateName: delegateRoot.stateName thumbnailImageSource: delegateRoot.stateImageSource whenCondition: delegateRoot.whenConditionString @@ -771,15 +791,16 @@ Rectangle { onClone: root.cloneState(delegateRoot.internalNodeId) onExtend: root.extendState(delegateRoot.internalNodeId) - onRemove: root.deleteState(delegateRoot.internalNodeId) + onRemove: { + if (delegateRoot.isDefault) + statesEditorModel.resetDefaultState() + + root.deleteState(delegateRoot.internalNodeId) + } onStateNameFinished: statesEditorModel.renameState( delegateRoot.internalNodeId, stateThumbnail.stateName) - - onWhenConditionFinished: statesEditorModel.setWhenCondition( - delegateRoot.internalNodeId, - stateThumbnail.whenCondition) } } } diff --git a/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml b/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml index a284131bad..1591d3ecb7 100644 --- a/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml +++ b/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml @@ -36,19 +36,19 @@ T.ScrollBar { implicitContentHeight + topPadding + bottomPadding) contentItem: Rectangle { - implicitWidth: scrollBar.interactive ? 6 : 2 - implicitHeight: scrollBar.interactive ? 6 : 2 + implicitWidth: scrollBar.interactive ? 14 : 8 + implicitHeight: scrollBar.interactive ? 14 : 8 radius: width / 2 opacity: 0.0 - color: scrollBar.pressed ? StudioTheme.Values.themeSliderActiveTrackHover - : StudioTheme.Values.themeSliderHandle + color: scrollBar.pressed ? StudioTheme.Values.themeScrollBarHandle //"#4C4C4C"//DARK + : StudioTheme.Values.themeScrollBarTrack //"#3E3E3E"//DARK states: State { name: "active" when: scrollBar.active && scrollBar.size < 1.0 PropertyChanges { target: scrollBar.contentItem - opacity: 0.75 + opacity: 0.9 } } diff --git a/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml b/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml index 642ebd972d..ce95aa2198 100644 --- a/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml +++ b/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml @@ -25,9 +25,9 @@ import QtQuick import QtQuick.Controls -import StudioTheme 1.0 as StudioTheme +import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls -import QtQuick.Layouts 6.0 +import StudioTheme 1.0 as StudioTheme Item { id: root @@ -55,6 +55,10 @@ Item { property bool hasWhenCondition: false + property bool scrollViewActive: false + + property Item dragParent + property int visualIndex: 0 property int internalNodeId @@ -65,7 +69,6 @@ Item { signal extend signal remove signal stateNameFinished - signal whenConditionFinished signal grabbing signal letGo @@ -88,7 +91,7 @@ Item { DragHandler { id: dragHandler - enabled: !root.baseState && !root.extendedState + enabled: !root.baseState && !root.extendedState && !root.scrollViewActive onGrabChanged: function (transition, point) { if (transition === PointerDevice.GrabPassive || transition === PointerDevice.GrabExclusive) @@ -148,13 +151,14 @@ Item { rows: 1 spacing: stateBackground.thumbSpacing - StudioControls.AbstractButton { + HelperWidgets.AbstractButton { id: defaultButton width: 50 height: stateBackground.controlHeight checkedInverted: true buttonIcon: qsTr("Default") iconFont: StudioTheme.Constants.font + tooltip: qsTr("Set State as default") onClicked: { root.defaultClicked() root.focusSignal() @@ -607,8 +611,16 @@ Item { running: false interval: 50 repeat: false - onTriggered: statesEditorModel.setWhenCondition(root.internalNodeId, - bindingEditor.newWhenCondition) + onTriggered: { + if (whenCondition.previousCondition === bindingEditor.newWhenCondition) + return + + if ( bindingEditor.newWhenCondition !== "") + statesEditorModel.setWhenCondition(root.internalNodeId, + bindingEditor.newWhenCondition) + else + statesEditorModel.resetWhenCondition(root.internalNodeId) + } } stateModelNodeProperty: statesEditorModel.stateModelNode(root.internalNodeId) @@ -634,6 +646,8 @@ Item { indicatorVisible: true indicator.icon.text: StudioTheme.Constants.edit indicator.onClicked: { + whenCondition.previousCondition = whenCondition.text + bindingEditor.showWidget() bindingEditor.text = whenCondition.text bindingEditor.prepareBindings() @@ -650,13 +664,18 @@ Item { } onEditingFinished: { - if (whenCondition.previousCondition === whenCondition.text) + // The check for contenxtMenuAboutToShow is necessary in order to make a the + // popup stay open if the when condition was changed. Otherwise editingFinished + // will be called and the model will be reset. The popup will trigger a focus + // change and editingFinished is triggered. + if (whenCondition.previousCondition === whenCondition.text + || whenCondition.contextMenuAboutToShow) return whenCondition.previousCondition = whenCondition.text if (whenCondition.text !== "") - root.whenConditionFinished() + statesEditorModel.setWhenCondition(root.internalNodeId, root.whenCondition) else statesEditorModel.resetWhenCondition(root.internalNodeId) @@ -750,6 +769,11 @@ Item { name: "drag" when: dragHandler.active + ParentChange { + target: root + parent: root.dragParent + } + AnchorChanges { target: root anchors.horizontalCenter: undefined diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml new file mode 100644 index 0000000000..9ff90a2b9f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Layouts +import HelperWidgets 2.0 +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Column { + anchors.left: parent.left + anchors.right: parent.right + + Section { + caption: qsTr("Timer") + + anchors.left: parent.left + anchors.right: parent.right + + SectionLayout { + PropertyLabel { + text: qsTr("Interval") + tooltip: qsTr("Sets the interval between triggers, in milliseconds.") + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + minimumValue: 0 + maximumValue: 9999999 + backendValue: backendValues.interval + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Repeat") + tooltip: qsTr("Sets whether the timer is triggered repeatedly at the specified interval or just once.") + } + + SecondColumnLayout { + CheckBox { + text: backendValues.repeat.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.repeat + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Running") + tooltip: qsTr("Sets whether the timer is running or not.") + } + + SecondColumnLayout { + CheckBox { + text: backendValues.running.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.running + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Triggered on start") + tooltip: qsTr("Sets the timer to trigger when started.") + } + + SecondColumnLayout { + CheckBox { + text: backendValues.triggeredOnStart.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.triggeredOnStart + } + + ExpandingSpacer {} + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml index e5b0b8f928..e2ab61056b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml @@ -135,6 +135,7 @@ Section { BoolButtonRowButton { visible: section.showEasingCurve buttonIcon: StudioTheme.Constants.curveDesigner + checkable: false EasingCurveEditor { id: easingCurveEditor diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 6db3bb22f7..a8459c29ea 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -266,15 +266,6 @@ SecondColumnLayout { id: spacer } - StudioControls.Menu { - id: contextMenu - - StudioControls.MenuItem { - text: qsTr("Open Color Dialog") - onTriggered: colorPalette.showColorDialog(colorEditor.color) - } - } - Component.onCompleted: popupLoader.determineActiveColorMode() onBackendValueChanged: { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index fca7cbef21..af47d06a66 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -178,6 +178,15 @@ T.Popup { } } + StudioControls.Menu { + id: contextMenu + + StudioControls.MenuItem { + text: qsTr("Open Color Dialog") + onTriggered: colorPalette.showColorDialog(colorEditor.color) + } + } + GradientModel { id: gradientModel anchorBackendProperty: anchorBackend diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 5a18d24846..f145b09877 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -61,7 +61,6 @@ RoundedPanel 2.0 RoundedPanel.qml ScrollView 2.0 ScrollView.qml SecondColumnLayout 2.0 SecondColumnLayout.qml Section 2.0 Section.qml -SearchBox 2.0 SearchBox.qml SectionLayout 2.0 SectionLayout.qml Spacer 2.0 Spacer.qml SpinBox 2.0 SpinBox.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml index 65968b9bfe..158f49f6c3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SearchBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml @@ -11,6 +11,8 @@ Item { property alias text: searchFilterText.text + signal searchChanged(string searchText); + function clear() { searchFilterText.text = ""; @@ -58,7 +60,7 @@ Item { selectByMouse: true hoverEnabled: true - onTextChanged: rootView.handleSearchfilterChanged(text) + onTextChanged: root.searchChanged(text) Label { text: StudioTheme.Constants.search diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml index 2ac98abbfc..4911bf1180 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml @@ -28,6 +28,8 @@ T.TextField { property string preFocusText: "" + property bool contextMenuAboutToShow: false + horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter @@ -40,7 +42,7 @@ T.TextField { readOnly: false selectByMouse: true - persistentSelection: focus // QTBUG-73807 + persistentSelection: contextMenu.visible || root.focus clip: true width: StudioTheme.Values.defaultControlWidth @@ -56,28 +58,28 @@ T.TextField { enabled: true hoverEnabled: true propagateComposedEvents: true - acceptedButtons: Qt.LeftButton | Qt.RightButton + acceptedButtons: Qt.NoButton cursorShape: Qt.PointingHandCursor - onPressed: function(mouse) { - if (mouse.button === Qt.RightButton) - contextMenu.popup(root) - - mouse.accepted = false - } } - onPersistentSelectionChanged: { - if (!persistentSelection) - root.deselect() + onPressed: function(event) { + if (event.button === Qt.RightButton) + contextMenu.popup(root) } ContextMenu { id: contextMenu myTextEdit: root + + onClosed: root.forceActiveFocus() + onAboutToShow: root.contextMenuAboutToShow = true + onAboutToHide: root.contextMenuAboutToShow = false } onActiveFocusChanged: { - if (root.activeFocus) + // OtherFocusReason in this case means, if the TextField gets focus after the context menu + // was closed due to an menu item click. + if (root.activeFocus && root.focusReason !== Qt.OtherFocusReason) root.preFocusText = root.text } @@ -143,7 +145,7 @@ T.TextField { states: [ State { name: "default" - when: root.enabled && !root.hover && !root.edit + when: root.enabled && !root.hover && !root.edit && !contextMenu.visible PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackground @@ -162,7 +164,7 @@ T.TextField { State { name: "globalHover" when: (actionIndicator.hover || translationIndicator.hover || indicator.hover) - && !root.edit && root.enabled + && !root.edit && root.enabled && !contextMenu.visible PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundGlobalHover @@ -177,7 +179,7 @@ T.TextField { State { name: "hover" when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover - && !indicator.hover && !root.edit && root.enabled + && !indicator.hover && !root.edit && root.enabled && !contextMenu.visible PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundHover @@ -191,7 +193,7 @@ T.TextField { }, State { name: "edit" - when: root.edit + when: root.edit || contextMenu.visible PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundInteraction diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index 878b2307c3..fea940d878 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -30,6 +30,7 @@ RealSpinBoxIndicator 1.0 RealSpinBoxIndicator.qml RealSpinBoxInput 1.0 RealSpinBoxInput.qml ScrollBar 1.0 ScrollBar.qml ScrollView 1.0 ScrollView.qml +SearchBox 1.0 SearchBox.qml SecondColumnLayout 1.0 SecondColumnLayout.qml Section 1.0 Section.qml SectionLabel 1.0 SectionLabel.qml diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index bb1d7bfec8..b762ccdcc9 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -84,8 +84,8 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ff404040 -DSscrollBarHandle=ff505050 +DSscrollBarTrack=ff3E3E3E +DSscrollBarHandle=ff4C4C4C DSsectionHeadBackground=ff1f1f1f diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index d0a2c1b3d6..9335513217 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -86,8 +86,8 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ff404040 -DSscrollBarHandle=ff505050 +DSscrollBarTrack=ff3E3E3E +DSscrollBarHandle=ff4C4C4C DSsectionHeadBackground=ff1f1f1f diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index 203ab5e415..1e28230331 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -88,8 +88,8 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ff404040 -DSscrollBarHandle=ff505050 +DSscrollBarTrack=ff3E3E3E +DSscrollBarHandle=ff4C4C4C DSsectionHeadBackground=ff1f1f1f diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 7d4c6d9608..45ffe8537d 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -82,8 +82,8 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ff404040 -DSscrollBarHandle=ff505050 +DSscrollBarTrack=ff3E3E3E +DSscrollBarHandle=ff4C4C4C DSsectionHeadBackground=ff1f1f1f diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index c1108ffc5e..4d5c0b2cd2 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -166,7 +166,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets() return {}; } -void AssetsLibraryWidget::handleSearchfilterChanged(const QString &filterText) +void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText) { if (filterText == m_filterText || (m_assetsModel->isEmpty() && filterText.contains(m_filterText))) return; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index f361c58e1a..d78345460f 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -56,7 +56,7 @@ public: Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos); Q_INVOKABLE void handleAddAsset(); - Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); Q_INVOKABLE void handleExtFilesDrop(const QList<QUrl> &simpleFilePaths, const QList<QUrl> &complexFilePaths, const QString &targetDirPath = {}); diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index ba507e2458..1d09f30d18 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -292,4 +292,68 @@ void ActionEditor::updateWindowName(const QString &targetName) } } +void ActionEditor::invokeEditor(SignalHandlerProperty signalHandler, + std::function<void(SignalHandlerProperty)> onReject, + QObject * parent) +{ + if (!signalHandler.isValid()) + return; + + ModelNode connectionNode = signalHandler.parentModelNode(); + if (!connectionNode.isValid()) + return; + + if (!connectionNode.bindingProperty("target").isValid()) + return; + + ModelNode targetNode = connectionNode.bindingProperty("target").resolveToModelNode(); + if (!targetNode.isValid()) + return; + + const QString source = signalHandler.source(); + + QPointer<ActionEditor> editor = new ActionEditor(parent); + + editor->showWidget(); + editor->setModelNode(connectionNode); + editor->setConnectionValue(source); + editor->prepareConnections(); + editor->updateWindowName(targetNode.validId() + "." + signalHandler.name()); + + QObject::connect(editor, &ActionEditor::accepted, [=]() { + if (!editor) + return; + if (editor->m_modelNode.isValid()) { + editor->m_modelNode.view()->executeInTransaction("ActionEditor::" + "invokeEditorAccepted", + [=]() { + editor->m_modelNode + .signalHandlerProperty( + signalHandler.name()) + .setSource( + editor->connectionValue()); + }); + } + + //closing editor widget somewhy triggers rejected() signal. Lets disconect before it affects us: + editor->disconnect(); + editor->deleteLater(); + }); + + QObject::connect(editor, &ActionEditor::rejected, [=]() { + if (!editor) + return; + + if (onReject) { + editor->m_modelNode.view()->executeInTransaction("ActionEditor::" + "invokeEditorOnRejectFunc", + [=]() { onReject(signalHandler); }); + } + + //closing editor widget somewhy triggers rejected() signal 2nd time. Lets disconect before it affects us: + editor->disconnect(); + editor->deleteLater(); + }); +} + } // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index ec2f8fb9c1..3cab2cef63 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -7,6 +7,7 @@ #include <bindingeditor/actioneditordialog.h> #include <qmldesignercorelib_global.h> #include <modelnode.h> +#include <signalhandlerproperty.h> #include <QtQml> #include <QObject> @@ -44,6 +45,10 @@ public: Q_INVOKABLE void updateWindowName(const QString &targetName = {}); + static void invokeEditor(SignalHandlerProperty signalHandler, + std::function<void(SignalHandlerProperty)> onReject = nullptr, + QObject *parent = nullptr); + signals: void accepted(); void rejected(); diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 1133af1065..c876ea170d 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -12,6 +12,7 @@ namespace ComponentCoreConstants { const char rootCategory[] = ""; const char selectionCategory[] = "Selection"; +const char connectionsCategory[] = "Connections"; const char arrangeCategory[] = "Arrange"; const char qmlPreviewCategory[] = "QmlPreview"; const char editCategory[] = "Edit"; @@ -76,6 +77,7 @@ const char openSignalDialogCommandId[] = "OpenSignalDialog"; const char update3DAssetCommandId[] = "Update3DAsset"; const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); +const char connectionsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connections"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); const char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select Effect"); const char arrangeCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Arrange"); @@ -183,6 +185,7 @@ const char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen const int priorityFirst = 280; const int prioritySelectionCategory = 220; +const int priorityConnectionsCategory = 210; const int priorityQmlPreviewCategory = 200; const int priorityStackCategory = 180; const int priorityEditCategory = 160; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 72b6f704c4..fb64084076 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -22,6 +22,7 @@ #include <documentmanager.h> #include <qmldesignerplugin.h> #include <viewmanager.h> +#include <actioneditor.h> #include <listmodeleditor/listmodeleditordialog.h> #include <listmodeleditor/listmodeleditormodel.h> @@ -433,6 +434,302 @@ public: } }; +QString prependSignal(QString signalHandlerName) +{ + if (signalHandlerName.isNull() || signalHandlerName.isEmpty()) + return {}; + + QChar firstChar = signalHandlerName.at(0).toUpper(); + signalHandlerName[0] = firstChar; + signalHandlerName.prepend(QLatin1String("on")); + + return signalHandlerName; +} + +QStringList getSignalsList(const ModelNode &node) +{ + if (!node.isValid()) + return {}; + + if (!node.hasMetaInfo()) + return {}; + + QStringList signalsList; + NodeMetaInfo nodeMetaInfo = node.metaInfo(); + + for (const auto &signalName : nodeMetaInfo.signalNames()) { + signalsList << QString::fromUtf8(signalName); + } + + //on...Changed are the most regular signals, we assign them the lowest priority, + //we don't need them right now +// QStringList signalsWithChanged = signalsList.filter("Changed"); + + //these are item specific, like MouseArea.clicked, they have higher priority + QStringList signalsWithoutChanged = signalsList; + signalsWithoutChanged.removeIf([](QString str) { + if (str.endsWith("Changed")) + return true; + return false; + }); + + QStringList finalResult; + finalResult.append(signalsWithoutChanged); + + + if (finalResult.isEmpty()) + finalResult = signalsList; + + finalResult.removeDuplicates(); + + return finalResult; +} + +struct SlotEntry +{ + QString category; + QString name; + std::function<void(SignalHandlerProperty)> action; +}; + +QList<SlotEntry> getSlotsLists(const ModelNode &node) +{ + if (!node.isValid()) + return {}; + + if (!node.view()->rootModelNode().isValid()) + return {}; + + QList<SlotEntry> resultList; + + ModelNode rootNode = node.view()->rootModelNode(); + QmlObjectNode rootObjectNode(rootNode); + + const QString stateCategory = "Change State"; + + //For now we are using category as part of the state name + //We should change it, once we extend number of categories + const SlotEntry defaultState = {stateCategory, + (stateCategory + " to " + "Default State"), + [rootNode](SignalHandlerProperty signalHandler) { + signalHandler.setSource( + QString("%1.state = \"\"").arg(rootNode.id())); + }}; + resultList.push_back(defaultState); + + for (const auto &stateName : rootObjectNode.states().names()) { + SlotEntry entry = {stateCategory, + (stateCategory + " to " + stateName), + [rootNode, stateName](SignalHandlerProperty signalHandler) { + signalHandler.setSource( + QString("%1.state = \"%2\"").arg(rootNode.id(), stateName)); + }}; + + resultList.push_back(entry); + } + + return resultList; +} + +//creates connection without signalHandlerProperty +ModelNode createNewConnection(ModelNode targetNode) +{ + NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->metaInfo("QtQuick.Connections"); + ModelNode newConnectionNode = targetNode.view() + ->createModelNode("QtQuick.Connections", + connectionsMetaInfo.majorVersion(), + connectionsMetaInfo.minorVersion()); + if (QmlItemNode::isValidQmlItemNode(targetNode)) + targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode); + + newConnectionNode.bindingProperty("target").setExpression(targetNode.id()); + + return newConnectionNode; +} + +void removeSignal(SignalHandlerProperty signalHandler) +{ + auto connectionNode = signalHandler.parentModelNode(); + auto connectionSignals = connectionNode.signalProperties(); + if (connectionSignals.size() > 1) { + if (connectionSignals.contains(signalHandler)) + connectionNode.removeProperty(signalHandler.name()); + } else { + connectionNode.destroy(); + } +} + +class ConnectionsModelNodeActionGroup : public ActionGroup +{ +public: + ConnectionsModelNodeActionGroup(const QString &displayName, + const QByteArray &menuId, + int priority) + : ActionGroup(displayName, + menuId, + priority, + &SelectionContextFunctors::always, + &SelectionContextFunctors::selectionEnabled) + {} + + void updateContext() override + { + menu()->clear(); + + const auto selection = selectionContext(); + if (!selection.isValid()) + return; + if (!selection.singleNodeIsSelected()) + return; + if (!action()->isEnabled()) + return; + + ModelNode currentNode = selection.currentSingleSelectedNode(); + QmlObjectNode currentObjectNode(currentNode); + + QStringList signalsList = getSignalsList(currentNode); + QList<SlotEntry> slotsList = getSlotsLists(currentNode); + currentNode.validId(); + + for (const ModelNode &connectionNode : currentObjectNode.getAllConnections()) { + for (const AbstractProperty &property : connectionNode.properties()) { + if (property.isSignalHandlerProperty() && property.name() != "target") { + const auto signalHandler = property.toSignalHandlerProperty(); + + const QString propertyName = QString::fromUtf8(signalHandler.name()); + + QMenu *activeSignalHandlerGroup = new QMenu(propertyName, menu()); + + QMenu *editSignalGroup = new QMenu("Change Signal", menu()); + + for (const auto &signalStr : signalsList) { + if (prependSignal(signalStr).toUtf8() == signalHandler.name()) + continue; + + ActionTemplate *newSignalAction = new ActionTemplate( + (signalStr + "Id").toLatin1(), + signalStr, + [signalStr, signalHandler](const SelectionContext &) { + signalHandler.parentModelNode().view()->executeInTransaction( + "ConnectionsModelNodeActionGroup::" + "changeSignal", + [signalStr, signalHandler]() { + auto connectionNode = signalHandler.parentModelNode(); + auto newHandler = connectionNode.signalHandlerProperty( + prependSignal(signalStr).toLatin1()); + newHandler.setSource(signalHandler.source()); + connectionNode.removeProperty(signalHandler.name()); + }); + }); + editSignalGroup->addAction(newSignalAction); + } + + activeSignalHandlerGroup->addMenu(editSignalGroup); + + if (!slotsList.isEmpty()) { + QMenu *editSlotGroup = new QMenu("Change Slot", menu()); + + for (const auto &slot : slotsList) { + ActionTemplate *newSlotAction = new ActionTemplate( + (slot.name + "Id").toLatin1(), + slot.name, + [slot, signalHandler](const SelectionContext &) { + signalHandler.parentModelNode() + .view() + ->executeInTransaction("ConnectionsModelNodeActionGroup::" + "changeSlot", + [slot, signalHandler]() { + slot.action(signalHandler); + }); + }); + editSlotGroup->addAction(newSlotAction); + } + activeSignalHandlerGroup->addMenu(editSlotGroup); + } + + ActionTemplate *openEditorAction = new ActionTemplate( + (propertyName + "OpenEditorId").toLatin1(), + QString( + QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), + [=](const SelectionContext &) { + signalHandler.parentModelNode().view()->executeInTransaction( + "ConnectionsModelNodeActionGroup::" + "openConnectionsEditor", + [signalHandler]() { ActionEditor::invokeEditor(signalHandler); }); + }); + + activeSignalHandlerGroup->addAction(openEditorAction); + + ActionTemplate *removeSignalHandlerAction = new ActionTemplate( + (propertyName + "RemoveSignalHandlerId").toLatin1(), + QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove this handler")), + [signalHandler](const SelectionContext &) { + signalHandler.parentModelNode().view()->executeInTransaction( + "ConnectionsModelNodeActionGroup::" + "removeSignalHandler", + [signalHandler]() { + removeSignal(signalHandler); + }); + }); + + activeSignalHandlerGroup->addAction(removeSignalHandlerAction); + + menu()->addMenu(activeSignalHandlerGroup); + } + } + } + + //singular add connection: + QMenu *addConnection = new QMenu(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", + "Add signal handler")), + menu()); + + for (const auto &signalStr : signalsList) { + QMenu *newSignal = new QMenu(signalStr, addConnection); + + for (const auto &slot : slotsList) { + ActionTemplate *newSlot = new ActionTemplate( + QString(signalStr + slot.name + "Id").toLatin1(), + slot.name, + [=](const SelectionContext &) { + currentNode.view()->executeInTransaction( + "ConnectionsModelNodeActionGroup::addConnection", [=]() { + ModelNode newConnectionNode = createNewConnection(currentNode); + slot.action(newConnectionNode.signalHandlerProperty( + prependSignal(signalStr).toLatin1())); + }); + }); + newSignal->addAction(newSlot); + } + + ActionTemplate *openEditorAction = new ActionTemplate( + (signalStr + "OpenEditorId").toLatin1(), + QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), + [=](const SelectionContext &) { + currentNode.view()->executeInTransaction( + "ConnectionsModelNodeActionGroup::" + "openConnectionsEditor", + [=]() { + ModelNode newConnectionNode = createNewConnection(currentNode); + + SignalHandlerProperty newHandler + = newConnectionNode.signalHandlerProperty( + prependSignal(signalStr).toLatin1()); + + newHandler.setSource( + QString("console.log(\"%1.%2\")").arg(currentNode.id(), signalStr)); + ActionEditor::invokeEditor(newHandler, removeSignal); + }); + }); + newSignal->addAction(openEditorAction); + + addConnection->addMenu(newSignal); + } + + menu()->addMenu(addConnection); + } +}; + class DocumentError : public std::exception { public: @@ -953,6 +1250,11 @@ void DesignerActionManager::createDefaultDesignerActions() selectionCategory, prioritySelectionCategory)); + addDesignerAction(new ConnectionsModelNodeActionGroup( + connectionsCategoryDisplayName, + connectionsCategory, + priorityConnectionsCategory)); + addDesignerAction(new ActionGroup( arrangeCategoryDisplayName, arrangeCategory, diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 48ef33ca39..fd6ef9f435 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -527,9 +527,17 @@ void DebugView::instancesToken(const QString &/*tokenName*/, int /*tokenNumber*/ } -void DebugView::currentStateChanged(const ModelNode &/*node*/) +void DebugView::currentStateChanged(const ModelNode &node) { + if (isDebugViewEnabled()) { + QTextStream message; + QString string; + message.setString(&string); + message << node; + + log("::currentStateChanged:", string); + } } void DebugView::nodeOrderChanged([[maybe_unused]] const NodeListProperty &listProperty) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 5a359693f2..cb955174af 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -205,7 +205,7 @@ QList<QToolButton *> ItemLibraryWidget::createToolBarWidgets() } -void ItemLibraryWidget::handleSearchfilterChanged(const QString &filterText) +void ItemLibraryWidget::handleSearchFilterChanged(const QString &filterText) { if (filterText != m_filterText) { m_filterText = filterText; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 696ce9f168..16673849be 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -69,7 +69,7 @@ public: Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos); Q_INVOKABLE void removeImport(const QString &importUrl); Q_INVOKABLE void addImportForItem(const QString &importUrl); - Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); Q_INVOKABLE void handleAddImport(int index); Q_INVOKABLE void goIntoComponent(const QString &source); diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp index 422288c8b2..7dae1e900f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp @@ -97,8 +97,9 @@ QString BundleImporter::importComponent(const QString &qmlFile, FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile)); const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); - m_pendingTypes.append(QStringLiteral("%1.%2") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), qmlType)); + m_pendingTypes.append(QStringLiteral("%1.%2.%3") + .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), + m_bundleId, qmlType)); if (!qmldirContent.contains(qmlFile)) { qmldirContent.append(qmlType); qmldirContent.append(" 1.0 "); @@ -159,10 +160,6 @@ QString BundleImporter::importComponent(const QString &qmlFile, // If import is not yet possible, import statement needs to be added asynchronously to // avoid errors, as code model update takes a while. m_importAddPending = true; - - // Full reset is not necessary if new import directory appearing will trigger scanning, - // but if directory existed but was not valid possible import, we need to do a reset. - m_fullReset = bundleImportPathExists; } } m_importTimerCount = 0; diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp index 75549945f4..b5bc19e785 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp @@ -30,9 +30,10 @@ namespace QmlDesigner { BundleMaterial::BundleMaterial(QObject *parent, const QString &name, const QString &qml, + const TypeName &type, const QUrl &icon, const QStringList &files) - : QObject(parent), m_name(name), m_qml(qml), m_icon(icon), m_files(files) {} + : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) {} bool BundleMaterial::filter(const QString &searchText) { @@ -54,6 +55,11 @@ QString BundleMaterial::qml() const return m_qml; } +TypeName BundleMaterial::type() const +{ + return m_type; +} + QStringList BundleMaterial::files() const { return m_files; diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h index 04f4ae2656..ae74a13d75 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h @@ -25,6 +25,8 @@ #pragma once +#include "qmldesignercorelib_global.h" + #include <QDataStream> #include <QObject> #include <QUrl> @@ -43,6 +45,7 @@ public: BundleMaterial(QObject *parent, const QString &name, const QString &qml, + const TypeName &type, const QUrl &icon, const QStringList &files); @@ -50,6 +53,7 @@ public: QUrl icon() const; QString qml() const; + TypeName type() const; QStringList files() const; bool visible() const; @@ -59,6 +63,7 @@ signals: private: QString m_name; QString m_qml; + TypeName m_type; QUrl m_icon; QStringList m_files; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp index 452093aba0..4e70008f1a 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp @@ -28,6 +28,7 @@ #include "bundleimporter.h" #include "bundlematerial.h" #include "bundlematerialcategory.h" +#include "qmldesignerconstants.h" #include "utils/qtcassert.h" #include <QCoreApplication> @@ -121,6 +122,8 @@ void MaterialBrowserBundleModel::loadMaterialBundle() m_matBundleExists = true; + const QString bundleId = m_matBundleObj.value("id").toString(); + const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); for (const QString &cat : categories) { @@ -136,8 +139,14 @@ void MaterialBrowserBundleModel::loadMaterialBundle() for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) files.append(asset.toString()); - auto bundleMat = new BundleMaterial(category, mat, matObj.value("qml").toString(), - QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())), files); + QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); + QString qml = matObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2.%3").arg( + QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + + auto bundleMat = new BundleMaterial(category, mat, qml, type, icon, files); category->addBundleMaterial(bundleMat); } @@ -149,7 +158,7 @@ void MaterialBrowserBundleModel::loadMaterialBundle() for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) sharedFiles.append(file.toString()); - m_importer = new Internal::BundleImporter(matBundleDir.path(), "MaterialBundle", sharedFiles); + m_importer = new Internal::BundleImporter(matBundleDir.path(), bundleId, sharedFiles); connect(m_importer, &Internal::BundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { if (metaInfo.isValid()) emit addBundleMaterialToProjectRequested(metaInfo); @@ -223,7 +232,10 @@ void MaterialBrowserBundleModel::applyToSelected(BundleMaterial *mat, bool add) void MaterialBrowserBundleModel::addMaterial(BundleMaterial *mat) { - m_importer->importComponent(mat->qml(), mat->files()); + QString err = m_importer->importComponent(mat->qml(), mat->files()); + + if (!err.isEmpty()) + qWarning() << __FUNCTION__ << err; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index bbe47ec94c..1f1dff4eef 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -266,8 +266,7 @@ void MaterialBrowserModel::removeMaterial(const ModelNode &material) void MaterialBrowserModel::deleteSelectedMaterial() { - if (isValidIndex(m_selectedIndex)) - m_materialList[m_selectedIndex].destroy(); + deleteMaterial(m_selectedIndex); } void MaterialBrowserModel::updateSelectedMaterial() @@ -365,7 +364,11 @@ void MaterialBrowserModel::pasteMaterialProperties(int idx) void MaterialBrowserModel::deleteMaterial(int idx) { - m_materialList[idx].destroy(); + if (isValidIndex(idx)) { + ModelNode node = m_materialList[idx]; + if (node.isValid()) + QmlObjectNode(node).destroy(); + } } void MaterialBrowserModel::renameMaterial(int idx, const QString &newName) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index f0bdf4d906..791b42a059 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -93,6 +93,8 @@ WidgetInfo MaterialBrowserView::widgetInfo() mat.setVariantProperty(prop.name(), prop.toVariantProperty().value()); else if (prop.isBindingProperty()) mat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); + else if (!all) + mat.removeProperty(prop.name()); } }); }); @@ -105,70 +107,23 @@ WidgetInfo MaterialBrowserView::widgetInfo() MaterialBrowserBundleModel *matBrowserBundleModel = m_widget->materialBrowserBundleModel().data(); connect(matBrowserBundleModel, &MaterialBrowserBundleModel::applyToSelectedTriggered, this, - [&] (BundleMaterial *material, bool add) { + [&] (BundleMaterial *bundleMat, bool add) { if (!m_selectedModel.isValid()) return; m_bundleMaterialDropTarget = m_selectedModel; m_bundleMaterialAddToSelected = add; - m_widget->materialBrowserBundleModel()->addMaterial(material); + + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialBrowserBundleModel()->addMaterial(bundleMat); }); connect(matBrowserBundleModel, &MaterialBrowserBundleModel::addBundleMaterialToProjectRequested, this, [&] (const QmlDesigner::NodeMetaInfo &metaInfo) { - ModelNode matLib = materialLibraryNode(); - if (!matLib.isValid()) - return; - - executeInTransaction("MaterialBrowserView::widgetInfo", [&] { - ModelNode newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), - metaInfo.minorVersion()); - matLib.defaultNodeListProperty().reparentHere(newMatNode); - - static QRegularExpression rgx("([A-Z])([a-z]*)"); - QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed(); - QString newId = model()->generateIdFromName(newName, "material"); - newMatNode.setIdWithRefactoring(newId); - - VariantProperty objNameProp = newMatNode.variantProperty("objectName"); - objNameProp.setValue(newName); - - if (m_bundleMaterialDropTarget.isValid()) { - QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget); - if (m_bundleMaterialAddToSelected) { - // TODO: unify this logic as it exist elsewhere also - auto expToList = [](const QString &exp) { - QString copy = exp; - copy = copy.remove("[").remove("]"); - - QStringList tmp = copy.split(',', Qt::SkipEmptyParts); - for (QString &str : tmp) - str = str.trimmed(); - - return tmp; - }; - - auto listToExp = [](QStringList &stringList) { - if (stringList.size() > 1) - return QString("[" + stringList.join(",") + "]"); - - if (stringList.size() == 1) - return stringList.first(); - - return QString(); - }; - QStringList matList = expToList(qmlObjNode.expression("materials")); - matList.append(newMatNode.id()); - QString updatedExp = listToExp(matList); - qmlObjNode.setBindingProperty("materials", updatedExp); - } else { - qmlObjNode.setBindingProperty("materials", newMatNode.id()); - } - m_bundleMaterialDropTarget = {}; - } - - m_bundleMaterialAddToSelected = false; - }); + applyBundleMaterialToDropTarget({}, metaInfo); }); } @@ -179,6 +134,74 @@ WidgetInfo MaterialBrowserView::widgetInfo() tr("Material Browser")); } +void MaterialBrowserView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat, + const NodeMetaInfo &metaInfo) +{ + if (!bundleMat.isValid() && !metaInfo.isValid()) + return; + + ModelNode matLib = materialLibraryNode(); + if (!matLib.isValid()) + return; + + executeInTransaction("MaterialBrowserView::applyBundleMaterialToDropTarget", [&] { + ModelNode newMatNode; + if (metaInfo.isValid()) { + newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), + metaInfo.minorVersion()); + matLib.defaultNodeListProperty().reparentHere(newMatNode); + + static QRegularExpression rgx("([A-Z])([a-z]*)"); + QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed(); + if (newName.endsWith(" Material")) + newName.chop(9); // remove trailing " Material" + QString newId = model()->generateIdFromName(newName, "material"); + newMatNode.setIdWithRefactoring(newId); + + VariantProperty objNameProp = newMatNode.variantProperty("objectName"); + objNameProp.setValue(newName); + } else { + newMatNode = bundleMat; + } + + if (m_bundleMaterialDropTarget.isValid()) { + QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget); + if (m_bundleMaterialAddToSelected) { + // TODO: unify this logic as it exist elsewhere also + auto expToList = [](const QString &exp) { + QString copy = exp; + copy = copy.remove("[").remove("]"); + + QStringList tmp = copy.split(',', Qt::SkipEmptyParts); + for (QString &str : tmp) + str = str.trimmed(); + + return tmp; + }; + + auto listToExp = [](QStringList &stringList) { + if (stringList.size() > 1) + return QString("[" + stringList.join(",") + "]"); + + if (stringList.size() == 1) + return stringList.first(); + + return QString(); + }; + QStringList matList = expToList(qmlObjNode.expression("materials")); + matList.append(newMatNode.id()); + QString updatedExp = listToExp(matList); + qmlObjNode.setBindingProperty("materials", updatedExp); + } else { + qmlObjNode.setBindingProperty("materials", newMatNode.id()); + } + + m_bundleMaterialDropTarget = {}; + m_bundleMaterialAddToSelected = false; + } + }); +} + void MaterialBrowserView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -347,6 +370,28 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups() m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); } +ModelNode MaterialBrowserView::getBundleMaterialDefaultInstance(const TypeName &type) +{ + const QList<ModelNode> materials = m_widget->materialBrowserModel()->materials(); + for (const ModelNode &mat : materials) { + if (mat.type() == type) { + bool isDefault = true; + const QList<AbstractProperty> props = mat.properties(); + for (const AbstractProperty &prop : props) { + if (prop.name() != "objectName") { + isDefault = false; + break; + } + } + + if (isDefault) + return mat; + } + } + + return {}; +} + void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &addedImports, [[maybe_unused]] const QList<Import> &removedImports) { @@ -383,7 +428,14 @@ void MaterialBrowserView::customNotification(const AbstractView *view, m_widget->materialBrowserModel()->deleteSelectedMaterial(); } else if (identifier == "drop_bundle_material") { m_bundleMaterialDropTarget = nodeList.first(); - m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial); + + + ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial); + m_draggedBundleMaterial = nullptr; } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index d163b5fa3f..31b0310f4c 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -3,7 +3,8 @@ #pragma once -#include <abstractview.h> +#include "abstractview.h" +#include "nodemetainfo.h" #include <QPointer> @@ -45,6 +46,8 @@ private: void refreshModel(bool updateImages); bool isMaterial(const ModelNode &node) const; void loadPropertyGroups(); + void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {}); + ModelNode getBundleMaterialDefaultInstance(const TypeName &type); QPointer<MaterialBrowserWidget> m_widget; ModelNode m_bundleMaterialDropTarget; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index c8473e5856..9df4a39b6e 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -192,7 +192,7 @@ void MaterialBrowserWidget::contextHelp(const Core::IContext::HelpCallback &call callback({}); } -void MaterialBrowserWidget::handleSearchfilterChanged(const QString &filterText) +void MaterialBrowserWidget::handleSearchFilterChanged(const QString &filterText) { if (filterText != m_filterText) { m_filterText = filterText; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index 1767286b49..925e7bc260 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -51,7 +51,7 @@ public: QPointer<MaterialBrowserBundleModel> materialBrowserBundleModel() const; void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap); - Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos); Q_INVOKABLE void startDragBundleMaterial(QmlDesigner::BundleMaterial *bundleMat, const QPointF &mousePos); diff --git a/src/plugins/qmldesigner/components/stateseditornew/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditornew/stateseditorview.cpp index 65d021eb05..a8e28f62e0 100644 --- a/src/plugins/qmldesigner/components/stateseditornew/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditornew/stateseditorview.cpp @@ -720,7 +720,7 @@ void StatesEditorView::propertiesRemoved(const QList<AbstractProperty>& property for (const AbstractProperty &property : propertyList) { if (property.name() == "states" && property.parentModelNode() == activeStateGroup().modelNode()) resetModel(); - if (property.name() == "when" + if ((property.name() == "when" || property.name() == "name") && QmlModelState::isValidQmlModelState(property.parentModelNode())) resetModel(); if (property.name() == "extend") @@ -848,7 +848,8 @@ void StatesEditorView::variantPropertiesChanged(const QList<VariantProperty> &pr auto guard = qScopeGuard([&]() { m_block = false; }); for (const VariantProperty &property : propertyList) { - if (property.name() == "name" && QmlModelState::isValidQmlModelState(property.parentModelNode())) + if (property.name() == "name" + && QmlModelState::isValidQmlModelState(property.parentModelNode())) resetModel(); else if (property.name() == "state" && property.parentModelNode() == activeStateGroup().modelNode()) diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp index 88aa4efd40..0a9cd7d6d3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp @@ -35,10 +35,12 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name, if (file.open()) { QString qtQuickVersion; QString qtQuick3DVersion; - QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target()->kit()); - if (qtVersion && qtVersion->qtVersion() < QVersionNumber(6, 0, 0)) { - qtQuickVersion = "2.15"; - qtQuick3DVersion = "1.15"; + if (target()) { + QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target()->kit()); + if (qtVersion && qtVersion->qtVersion() < QVersionNumber(6, 0, 0)) { + qtQuickVersion = "2.15"; + qtQuick3DVersion = "1.15"; + } } QString content{ diff --git a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h index 547a660361..e4dc67cd5d 100644 --- a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h +++ b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h @@ -3,6 +3,8 @@ #ifndef STYLESHEETMERGER_H #define STYLESHEETMERGER_H +#include "qmldesignercorelib_global.h" + #include <QString> #include <QHash> #include <modelnode.h> @@ -19,8 +21,8 @@ struct ReparentInfo { bool alreadyReparented; }; - -class StylesheetMerger { +class QMLDESIGNERCORE_EXPORT StylesheetMerger +{ public: StylesheetMerger(AbstractView*, AbstractView*); void merge(); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index aac2e6fd11..f3790dad83 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -5,13 +5,13 @@ #include "texttomodelmerger.h" #include "modeltotextmerger.h" +#include "model_p.h" #include <bindingproperty.h> #include <customnotifications.h> #include <filemanager/astobjecttextextractor.h> #include <filemanager/firstdefinitionfinder.h> #include <filemanager/objectlengthcalculator.h> -#include <model_p.h> #include <modelnode.h> #include <modelnodepositionstorage.h> #include <nodeproperty.h> diff --git a/src/plugins/qmldesigner/qtquickplugin/images/timer-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/timer-16px.png Binary files differnew file mode 100644 index 0000000000..c675d5a707 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/timer-16px.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/timer-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/timer-24px.png Binary files differnew file mode 100644 index 0000000000..bd9419aaa0 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/timer-24px.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/timer-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/timer-24px@2x.png Binary files differnew file mode 100644 index 0000000000..ff2d487cc9 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/timer-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 7cdc3309cb..1e78c02b77 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -98,5 +98,8 @@ <file>images/timeline-16px.png</file> <file>images/keyframe-16px.png</file> <file>images/timeline-animation-16px.png</file> + <file>images/timer-16px.png</file> + <file>images/timer-24px.png</file> + <file>images/timer-24px@2x.png</file> </qresource> </RCC> diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 4b766f715c..c45db41b8d 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -488,6 +488,25 @@ MetaInfo { } Type { + name: "QtQml.Timer" + icon: ":/qtquickplugin/images/timer-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Timer" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/timer-24px.png" + version: "2.0" + } + } + + Type { name: "QtQml.Component" icon: ":/qtquickplugin/images/component-icon16.png" diff --git a/src/plugins/studiowelcome/userpresets.cpp b/src/plugins/studiowelcome/userpresets.cpp index a643df3fdd..86fdb2e557 100644 --- a/src/plugins/studiowelcome/userpresets.cpp +++ b/src/plugins/studiowelcome/userpresets.cpp @@ -21,7 +21,14 @@ FileStoreIo::FileStoreIo(const QString &fileName) QByteArray FileStoreIo::read() const { - m_file->open(QFile::ReadOnly | QFile::Text); + if (!m_file->exists()) + return {}; + + if (!m_file->open(QFile::ReadOnly | QFile::Text)) { + qWarning() << "Cannot load User Preset(s)"; + return {}; + } + QByteArray data = m_file->readAll(); m_file->close(); @@ -30,7 +37,11 @@ QByteArray FileStoreIo::read() const void FileStoreIo::write(const QByteArray &data) { - m_file->open(QFile::WriteOnly | QFile::Text); + if (!m_file->open(QFile::WriteOnly | QFile::Text)) { + qWarning() << "Cannot save User Preset(s)"; + return; + } + m_file->write(data); m_file->close(); } |