diff --git a/doc/qtcreator/src/qtquick/qt-design-viewer.qdoc b/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc similarity index 74% rename from doc/qtcreator/src/qtquick/qt-design-viewer.qdoc rename to doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc index 2871f788f14..3cb1c61e1b7 100644 --- a/doc/qtcreator/src/qtquick/qt-design-viewer.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc @@ -3,13 +3,8 @@ /*! \page qt-design-viewer.html - \if defined(qtdesignstudio) - \previouspage creator-live-preview-android.html - \nextpage studio-exporting-and-importing.html - \else \previouspage creator-live-preview-devices.html \nextpage creator-building-targets.html - \endif \title Previewing in Browsers @@ -24,24 +19,17 @@ However, the actual performance of the application once started is indistinguishable from the same application running on the desktop. - \if defined(qtdesignstudio) - To create a resource file out of your project, select \uicontrol Build > - \uicontrol {Generate Resource File} in \QC. Then upload the package into - \QDV. - \else You can run \l{Creating Qt Quick UI Projects}{Qt Quick UI projects}, which have a .qmlproject file that define the main QML file and the import paths. Compress the project folder into a ZIP file that you upload to \QDV. - \endif The loaded applications remain locally in your browser. No data is uploaded into the cloud. To preview an application in a web browser: - \list - \li In the browser, open \l{http://qt-webassembly.io/designviewer/} - {\QDV}. + \list 1 + \li In the browser, open \l{ https://designviewer.qt.io/}{\QDV}. \li Drag and drop your application package to \QDV, or click the load icon to browse for your file. \endlist diff --git a/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc b/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc index f19ddf7e1cc..95cd83edea5 100644 --- a/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc @@ -57,10 +57,15 @@ You can preview Android applications live using an Android emulator. - \endif + \li \l{Sharing Applications Online} + + You can share applications online and view them in a web browser. + + \else \li \l{Previewing in Browsers} - You can open \l{https://qt-webassembly.io/designviewer/}{\QDV} + You can open \l{https://designviewer.qt.io/}{\QDV} in a browser and load applications to it. + \endif \endlist */ diff --git a/doc/qtdesignstudio/images/extended-state.webp b/doc/qtdesignstudio/images/extended-state.webp new file mode 100644 index 00000000000..58eae1febfc Binary files /dev/null and b/doc/qtdesignstudio/images/extended-state.webp differ diff --git a/doc/qtdesignstudio/images/no-extended-state.webp b/doc/qtdesignstudio/images/no-extended-state.webp new file mode 100644 index 00000000000..41fe322f0db Binary files /dev/null and b/doc/qtdesignstudio/images/no-extended-state.webp differ diff --git a/doc/qtdesignstudio/images/qmldesigner-screen-design.png b/doc/qtdesignstudio/images/qmldesigner-screen-design.png index 4ef84082d51..08192e0b4fa 100644 Binary files a/doc/qtdesignstudio/images/qmldesigner-screen-design.png and b/doc/qtdesignstudio/images/qmldesigner-screen-design.png differ diff --git a/doc/qtdesignstudio/images/qmldesigner-states.png b/doc/qtdesignstudio/images/qmldesigner-states.png index 2b6e2794e13..552c644e647 100644 Binary files a/doc/qtdesignstudio/images/qmldesigner-states.png and b/doc/qtdesignstudio/images/qmldesigner-states.png differ diff --git a/doc/qtdesignstudio/images/qmldesigner-transitions.png b/doc/qtdesignstudio/images/qmldesigner-transitions.png index 9c91383878f..4142cacee63 100644 Binary files a/doc/qtdesignstudio/images/qmldesigner-transitions.png and b/doc/qtdesignstudio/images/qmldesigner-transitions.png differ diff --git a/doc/qtdesignstudio/images/qt-design-viewer.webp b/doc/qtdesignstudio/images/qt-design-viewer.webp new file mode 100644 index 00000000000..47c80e4d6ad Binary files /dev/null and b/doc/qtdesignstudio/images/qt-design-viewer.webp differ diff --git a/doc/qtdesignstudio/images/share-online-manage.webp b/doc/qtdesignstudio/images/share-online-manage.webp new file mode 100644 index 00000000000..3ddb938dfcb Binary files /dev/null and b/doc/qtdesignstudio/images/share-online-manage.webp differ diff --git a/doc/qtdesignstudio/images/share-online.webp b/doc/qtdesignstudio/images/share-online.webp new file mode 100644 index 00000000000..c91661f18cb Binary files /dev/null and b/doc/qtdesignstudio/images/share-online.webp differ diff --git a/doc/qtdesignstudio/images/states-view-menu.png b/doc/qtdesignstudio/images/states-view-menu.png new file mode 100644 index 00000000000..6f937749048 Binary files /dev/null and b/doc/qtdesignstudio/images/states-view-menu.png differ diff --git a/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png b/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png index 2579687674e..3b2b8b173ac 100644 Binary files a/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png and b/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png differ diff --git a/doc/qtdesignstudio/images/timeline-states.png b/doc/qtdesignstudio/images/timeline-states.png index a1dc73e51eb..0ef1f7da959 100644 Binary files a/doc/qtdesignstudio/images/timeline-states.png and b/doc/qtdesignstudio/images/timeline-states.png differ diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc new file mode 100644 index 00000000000..9d869a72d0d --- /dev/null +++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc @@ -0,0 +1,43 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qt-design-viewer.html + \previouspage creator-live-preview-android.html + \nextpage studio-exporting-and-importing.html + + \title Sharing Applications Online + + \image qt-design-viewer.webp + + In \QDS, you can share your applications in most widely-used web browsers, + such as Apple Safari, Google Chrome, Microsoft Edge, and Mozilla Firefox, + on the desktop and on mobile devices. + + The application shared online runs in Qt Design Viewer, which is a QML + viewer that runs in your web browser. + + The startup and compilation time depend on your browser and configuration. + However, the actual performance of the application once started is + indistinguishable from the same application running on the desktop. + + The loaded applications remain locally in your browser. No data is uploaded + into the cloud. + + \section1 Sharing your Application Online + + To share your \QDS application online: + + \list 1 + \li Open the application in \QDS. + \li Select \uicontrol File > \uicontrol {Share Application Online}. + \li In the dialog, select \uicontrol Share. + \image share-online.webp + \endlist + + In the dialog, you can now open the application in a web + browser, copy the link to share with others, or manage your shared + applications. + + \image share-online-manage.webp +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 567bb6fcb21..e80d3750345 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -131,7 +131,7 @@ \li \l{Previewing on Desktop} \li \l{Previewing on Devices} \li \l{Previewing Android Applications} - \li \l{Previewing in Browsers} + \li \l{Sharing Applications Online} \endlist \li \l {Asset Creation with Other Tools} \list diff --git a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc index 884b1804618..407e2f57697 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc @@ -5,6 +5,7 @@ \page qtquick-states-view.html \previouspage qtquick-connection-view.html \nextpage studio-translations.html + \sa {Working with States} \title States @@ -25,48 +26,4 @@ \youtube FzmLuRHQXaw - \section1 Summary of States View Actions - - To open the \uicontrol Actions menu, select - \inlineimage icons/action-icon.png - . The actions available in the menu depend on the current context. For - example, the option for editing an annotation becomes available after - you add an annotation. - - \table - \header - \li Action - \li Purpose - \li Read More - \row - \li \uicontrol {Set when Condition} - \li Determines when a state should be applied. - \li \l{Applying States} - \row - \li \uicontrol {Reset when Condition} - \li Removes \c when condition for the state. - \li \l{Applying States} - \row - \li \uicontrol {Set as Default} - \li Sets the current state as the startup state of the application. - \li \l{Setting the Default State} - \row - \li\uicontrol {Reset Default} - \li Resets the current state as the default state. - \li \l{Setting the Default State} - \row - \li \uicontrol {Add Annotation} - \li Opens the \uicontrol {Annotation Editor} when you can add an - annotation for the states that you create. - \li \l{Annotating Designs} - \row - \li \uicontrol {Edit Annotation} - \li Opens the \uicontrol {Annotation Editor} where you can edit the - annotation for the state. - \li \l{Annotating Designs} - \row - \li \uicontrol {Add Annotation} - \li Removes the annotation for the state. - \li \l{Annotating Designs} - \endtable */ diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index e4e23f899ca..aee10e2c5ba 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -19,9 +19,9 @@ \image qmldesigner-transitions.png "States view" - Click the new state to switch to it in the \l {2D} view, and then modify the - values of the properties of components or component instances in - \l Properties. + Click the new state to switch to it in the \l {2D} and \l{3D} views, and + then modify the values of the properties of components or component + instances in \l Properties. For example, to change the appearance of a button, you can define states in the button component to hide the button image and show another image in its @@ -60,21 +60,17 @@ \section1 Setting the Default State - To determine the startup state of the application, - select \inlineimage icons/action-icon.png - to open the \uicontrol Actions menu, and then select - \uicontrol {Set as Default}. + The default state determines the startup state of the application. - To reset the state later, select \uicontrol Actions > - \uicontrol {Reset Default}. + To set a state to the default state, select \uicontrol Default. \section1 Applying States - To determine when a state should be applied, select \uicontrol Actions > - \uicontrol {Set when Condition}. In \uicontrol {Binding Editor}, specify - a \l [QtQuick]{State::when}{when} property for the state. Set the value of - the property to a boolean expression that evaluates to \c true when you want - the state to be applied. + To determine when a state is applied, select \inlineimage icons/edit.png + in the \uicontrol {When Condition} field. In \uicontrol {Binding Editor}, + specify a \l [QtQuick]{State::when}{when} property for the state. Set the + value of the property to a boolean expression that evaluates to \c true when + you want the state to be applied. This enables you to evaluate the truthfulness of several components' properties and move the UI to the state in which these conditions apply. @@ -170,10 +166,40 @@ \li Create additional states for each view and set the visibility or opacity of the components in the view. \li To determine which state is applied when the application starts, - select \uicontrol Actions > \uicontrol {Set as Default}. + select \uicontrol Default. \endlist \if defined(qtcreator) \include qtquick-states-scxml.qdocinc scxml state machines \endif + + \section1 State Groups + + With state groups, you can change the state of certain components + independently of other components and their states in the same view. + + Each state group has its own property changes and transitions. + + By default, there is one root state group. + + \section1 Extending States + + When a state extends another state, it inherits all the changes of that + state. The state being extended is treated as the base state in regards to + the changes specified by the extending state. + + Using extended states can make the user interface and your QML code + cleaner. + + Below is an example where the only change between \e State1 and \e State2 + is the brightness of \e directionalLight. + + Here, extended states are not used: + + \image no-extended-state.webp + + Here, \e State2 is extended from \e State1: + + \image extended-state.webp + */ diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml index e1c017814d6..77a784d92d5 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml @@ -162,6 +162,7 @@ ScrollView { // TODO: experiment using ListView instead of ScrollView + Column readonly property string suffix: fileName.substr(-4) readonly property bool isFont: suffix === ".ttf" || suffix === ".otf" + readonly property bool isEffect: suffix === ".qep" property bool currFileSelected: false MouseArea { @@ -180,7 +181,7 @@ ScrollView { // TODO: experiment using ListView instead of ScrollView + Column allowTooltip = true } onPositionChanged: tooltipBackend.reposition() - onPressed: (mouse)=> { + onPressed: (mouse) => { forceActiveFocus() allowTooltip = false tooltipBackend.hideTooltip() @@ -212,7 +213,7 @@ ScrollView { // TODO: experiment using ListView instead of ScrollView + Column } } - onReleased: (mouse)=> { + onReleased: (mouse) => { allowTooltip = true if (mouse.button === Qt.LeftButton) { if (!(mouse.modifiers & Qt.ControlModifier)) @@ -222,6 +223,14 @@ ScrollView { // TODO: experiment using ListView instead of ScrollView + Column } } + onDoubleClicked: (mouse) => { + forceActiveFocus() + allowTooltip = false + tooltipBackend.hideTooltip() + if (mouse.button === Qt.LeftButton && isEffect) + rootView.openEffectMaker(filePath) + } + ToolTip { visible: !isFont && mouseArea.containsMouse && !contextMenu.visible text: filePath diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index e6b978095dc..2b921374ae6 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -241,10 +241,15 @@ Item { delegate: Section { width: root.width - caption: bundleCategory + caption: bundleCategoryName addTopPadding: false sectionBackgroundColor: "transparent" visible: bundleCategoryVisible + expanded: bundleCategoryExpanded + expandOnClick: false + onToggleExpand: bundleCategoryExpanded = !bundleCategoryExpanded + onExpand: bundleCategoryExpanded = true + onCollapse: bundleCategoryExpanded = false Grid { width: scrollView.width @@ -254,7 +259,7 @@ Item { columns: root.width / root.cellWidth Repeater { - model: bundleMaterialsModel + model: bundleCategoryMaterials delegate: BundleMaterialItem { width: root.cellWidth diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml index d1e9f1ab1f4..796bcab6ebd 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml @@ -80,6 +80,10 @@ StudioControls.Menu { root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.principledMaterialSections); break; + case "SpecularGlossyMaterial": + root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.specularGlossyMaterialSections); + break; + case "CustomMaterial": root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.customMaterialSections); break; diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml index 64bc9b9d62b..8ec4e24b255 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml @@ -401,6 +401,7 @@ Item { ListElement { name: "Qt 5.15" } ListElement { name: "Qt 6.2" } ListElement { name: "Qt 6.3" } + ListElement { name: "Qt 6.4" } } onActivated: (index) => { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 69a10570f84..d175b4b76cb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -50,10 +50,19 @@ Item { Connections { target: Controller function onCollapseAll() { - if (collapsible) - section.expanded = false + if (collapsible) { + if (section.expandOnClick) + section.expanded = false + else + section.collapse() + } + } + function onExpandAll() { + if (section.expandOnClick) + section.expanded = true + else + section.expand() } - function onExpandAll() { section.expanded = true } } signal drop(var drag) @@ -61,6 +70,8 @@ Item { signal dropExit() signal showContextMenu() signal toggleExpand() + signal expand() + signal collapse() DropArea { id: dropArea diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index bc5fc4c538c..9339fda0daa 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -49,126 +49,128 @@ QtObject { readonly property string centerHorizontal: "\u0042" readonly property string centerVertical: "\u0043" readonly property string closeCross: "\u0044" - readonly property string colorPopupClose: "\u0045" - readonly property string columnsAndRows: "\u0046" - readonly property string copyStyle: "\u0047" - readonly property string cornerA: "\u0048" - readonly property string cornerB: "\u0049" - readonly property string cornersAll: "\u004A" - readonly property string curveDesigner: "\u004B" - readonly property string curveEditor: "\u004C" - readonly property string customMaterialEditor: "\u004D" - readonly property string decisionNode: "\u004E" - readonly property string deleteColumn: "\u004F" - readonly property string deleteMaterial: "\u0050" - readonly property string deleteRow: "\u0051" - readonly property string deleteTable: "\u0052" - readonly property string detach: "\u0053" - readonly property string distributeBottom: "\u0054" - readonly property string distributeCenterHorizontal: "\u0055" - readonly property string distributeCenterVertical: "\u0056" - readonly property string distributeLeft: "\u0057" - readonly property string distributeOriginBottomRight: "\u0058" - readonly property string distributeOriginCenter: "\u0059" - readonly property string distributeOriginNone: "\u005A" - readonly property string distributeOriginTopLeft: "\u005B" - readonly property string distributeRight: "\u005C" - readonly property string distributeSpacingHorizontal: "\u005D" - readonly property string distributeSpacingVertical: "\u005E" - readonly property string distributeTop: "\u005F" - readonly property string download: "\u0060" - readonly property string downloadUnavailable: "\u0061" - readonly property string downloadUpdate: "\u0062" - readonly property string downloaded: "\u0063" - readonly property string edit: "\u0064" - readonly property string eyeDropper: "\u0065" - readonly property string favorite: "\u0066" - readonly property string flowAction: "\u0067" - readonly property string flowTransition: "\u0068" - readonly property string fontStyleBold: "\u0069" - readonly property string fontStyleItalic: "\u006A" - readonly property string fontStyleStrikethrough: "\u006B" - readonly property string fontStyleUnderline: "\u006C" - readonly property string gradient: "\u006D" - readonly property string gridView: "\u006E" - readonly property string idAliasOff: "\u006F" - readonly property string idAliasOn: "\u0070" - readonly property string imported: "\u0071" - readonly property string infinity: "\u0072" - readonly property string keyframe: "\u0073" - readonly property string linkTriangle: "\u0074" - readonly property string linked: "\u0075" - readonly property string listView: "\u0076" - readonly property string lockOff: "\u0077" - readonly property string lockOn: "\u0078" - readonly property string materialPreviewEnvironment: "\u0079" - readonly property string materialPreviewModel: "\u007A" - readonly property string mergeCells: "\u007B" - readonly property string minus: "\u007C" - readonly property string mirror: "\u007D" - readonly property string newMaterial: "\u007E" - readonly property string openMaterialBrowser: "\u007F" - readonly property string orientation: "\u0080" - readonly property string paddingEdge: "\u0081" - readonly property string paddingFrame: "\u0082" - readonly property string pasteStyle: "\u0083" - readonly property string pause: "\u0084" - readonly property string pin: "\u0085" - readonly property string play: "\u0086" - readonly property string plus: "\u0087" - readonly property string promote: "\u0088" - readonly property string readOnly: "\u0089" - readonly property string redo: "\u008A" - readonly property string rotationFill: "\u008B" - readonly property string rotationOutline: "\u008C" - readonly property string search: "\u008D" - readonly property string sectionToggle: "\u008E" - readonly property string splitColumns: "\u008F" - readonly property string splitRows: "\u0090" - readonly property string startNode: "\u0091" - readonly property string testIcon: "\u0092" - readonly property string textAlignBottom: "\u0093" - readonly property string textAlignCenter: "\u0094" - readonly property string textAlignJustified: "\u0095" - readonly property string textAlignLeft: "\u0096" - readonly property string textAlignMiddle: "\u0097" - readonly property string textAlignRight: "\u0098" - readonly property string textAlignTop: "\u0099" - readonly property string textBulletList: "\u009A" - readonly property string textFullJustification: "\u009B" - readonly property string textNumberedList: "\u009D" - readonly property string tickIcon: "\u009E" - readonly property string translationCreateFiles: "\u009F" - readonly property string translationCreateReport: "\u00A0" - readonly property string translationExport: "\u00A1" - readonly property string translationImport: "\u00A2" - readonly property string translationSelectLanguages: "\u00A3" - readonly property string translationTest: "\u00A4" - readonly property string transparent: "\u00A5" - readonly property string triState: "\u00A6" - readonly property string triangleArcA: "\u00A7" - readonly property string triangleArcB: "\u00A8" - readonly property string triangleCornerA: "\u00A9" - readonly property string triangleCornerB: "\u00AA" - readonly property string unLinked: "\u00AB" - readonly property string undo: "\u00AC" - readonly property string unpin: "\u00AE" - readonly property string upDownIcon: "\u00AF" - readonly property string upDownSquare2: "\u00B0" - readonly property string visibilityOff: "\u00B1" - readonly property string visibilityOn: "\u00B2" - readonly property string wildcard: "\u00B3" - readonly property string wizardsAutomotive: "\u00B4" - readonly property string wizardsDesktop: "\u00B5" - readonly property string wizardsGeneric: "\u00B6" - readonly property string wizardsMcuEmpty: "\u00B7" - readonly property string wizardsMcuGraph: "\u00B8" - readonly property string wizardsMobile: "\u00B9" - readonly property string wizardsUnknown: "\u00BA" - readonly property string zoomAll: "\u00BB" - readonly property string zoomIn: "\u00BC" - readonly property string zoomOut: "\u00BD" - readonly property string zoomSelection: "\u00BE" + readonly property string closeLink: "\u0045" + readonly property string colorPopupClose: "\u0046" + readonly property string columnsAndRows: "\u0047" + readonly property string copyLink: "\u0048" + readonly property string copyStyle: "\u0049" + readonly property string cornerA: "\u004A" + readonly property string cornerB: "\u004B" + readonly property string cornersAll: "\u004C" + readonly property string curveDesigner: "\u004D" + readonly property string curveEditor: "\u004E" + readonly property string customMaterialEditor: "\u004F" + readonly property string decisionNode: "\u0050" + readonly property string deleteColumn: "\u0051" + readonly property string deleteMaterial: "\u0052" + readonly property string deleteRow: "\u0053" + readonly property string deleteTable: "\u0054" + readonly property string detach: "\u0055" + readonly property string distributeBottom: "\u0056" + readonly property string distributeCenterHorizontal: "\u0057" + readonly property string distributeCenterVertical: "\u0058" + readonly property string distributeLeft: "\u0059" + readonly property string distributeOriginBottomRight: "\u005A" + readonly property string distributeOriginCenter: "\u005B" + readonly property string distributeOriginNone: "\u005C" + readonly property string distributeOriginTopLeft: "\u005D" + readonly property string distributeRight: "\u005E" + readonly property string distributeSpacingHorizontal: "\u005F" + readonly property string distributeSpacingVertical: "\u0060" + readonly property string distributeTop: "\u0061" + readonly property string download: "\u0062" + readonly property string downloadUnavailable: "\u0063" + readonly property string downloadUpdate: "\u0064" + readonly property string downloaded: "\u0065" + readonly property string edit: "\u0066" + readonly property string eyeDropper: "\u0067" + readonly property string favorite: "\u0068" + readonly property string flowAction: "\u0069" + readonly property string flowTransition: "\u006A" + readonly property string fontStyleBold: "\u006B" + readonly property string fontStyleItalic: "\u006C" + readonly property string fontStyleStrikethrough: "\u006D" + readonly property string fontStyleUnderline: "\u006E" + readonly property string gradient: "\u006F" + readonly property string gridView: "\u0070" + readonly property string idAliasOff: "\u0071" + readonly property string idAliasOn: "\u0072" + readonly property string infinity: "\u0073" + readonly property string keyframe: "\u0074" + readonly property string linkTriangle: "\u0075" + readonly property string linked: "\u0076" + readonly property string listView: "\u0077" + readonly property string lockOff: "\u0078" + readonly property string lockOn: "\u0079" + readonly property string materialPreviewEnvironment: "\u007A" + readonly property string materialPreviewModel: "\u007B" + readonly property string mergeCells: "\u007C" + readonly property string minus: "\u007D" + readonly property string mirror: "\u007E" + readonly property string newMaterial: "\u007F" + readonly property string openLink: "\u0080" + readonly property string openMaterialBrowser: "\u0081" + readonly property string orientation: "\u0082" + readonly property string paddingEdge: "\u0083" + readonly property string paddingFrame: "\u0084" + readonly property string pasteStyle: "\u0085" + readonly property string pause: "\u0086" + readonly property string pin: "\u0087" + readonly property string play: "\u0088" + readonly property string plus: "\u0089" + readonly property string promote: "\u008A" + readonly property string readOnly: "\u008B" + readonly property string redo: "\u008C" + readonly property string rotationFill: "\u008D" + readonly property string rotationOutline: "\u008E" + readonly property string search: "\u008F" + readonly property string sectionToggle: "\u0090" + readonly property string splitColumns: "\u0091" + readonly property string splitRows: "\u0092" + readonly property string startNode: "\u0093" + readonly property string testIcon: "\u0094" + readonly property string textAlignBottom: "\u0095" + readonly property string textAlignCenter: "\u0096" + readonly property string textAlignJustified: "\u0097" + readonly property string textAlignLeft: "\u0098" + readonly property string textAlignMiddle: "\u0099" + readonly property string textAlignRight: "\u009A" + readonly property string textAlignTop: "\u009B" + readonly property string textBulletList: "\u009D" + readonly property string textFullJustification: "\u009E" + readonly property string textNumberedList: "\u009F" + readonly property string tickIcon: "\u00A0" + readonly property string translationCreateFiles: "\u00A1" + readonly property string translationCreateReport: "\u00A2" + readonly property string translationExport: "\u00A3" + readonly property string translationImport: "\u00A4" + readonly property string translationSelectLanguages: "\u00A5" + readonly property string translationTest: "\u00A6" + readonly property string transparent: "\u00A7" + readonly property string triState: "\u00A8" + readonly property string triangleArcA: "\u00A9" + readonly property string triangleArcB: "\u00AA" + readonly property string triangleCornerA: "\u00AB" + readonly property string triangleCornerB: "\u00AC" + readonly property string unLinked: "\u00AE" + readonly property string undo: "\u00AF" + readonly property string unpin: "\u00B0" + readonly property string upDownIcon: "\u00B1" + readonly property string upDownSquare2: "\u00B2" + readonly property string visibilityOff: "\u00B3" + readonly property string visibilityOn: "\u00B4" + readonly property string wildcard: "\u00B5" + readonly property string wizardsAutomotive: "\u00B6" + readonly property string wizardsDesktop: "\u00B7" + readonly property string wizardsGeneric: "\u00B8" + readonly property string wizardsMcuEmpty: "\u00B9" + readonly property string wizardsMcuGraph: "\u00BA" + readonly property string wizardsMobile: "\u00BB" + readonly property string wizardsUnknown: "\u00BC" + readonly property string zoomAll: "\u00BD" + readonly property string zoomIn: "\u00BE" + readonly property string zoomOut: "\u00BF" + readonly property string zoomSelection: "\u00C0" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index e3b40281c82..acd8df6ce3f 100644 Binary files a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf and b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf differ diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index 3510d3e190b..6c6949442c6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -264,6 +264,14 @@ 'TargetQuickVersion': '6.3', 'TargetQuick3DVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4', + 'TargetQuick3DVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 68ffb6cff17..235116a1f4c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -261,6 +261,13 @@ "({ 'TargetQuickVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index 206d8272da6..2e933c76136 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -71,6 +71,7 @@ Project { Environment { QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" @if %{IsQt6Project} @else QMLSCENE_CORE_PROFILE: "true" // Required for macOS, but can create issues on embedded Linux @@ -99,7 +100,7 @@ Project { /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" - qdsVersion: "3.7" + qdsVersion: "3.8" quickVersion: "%{QtQuickVersion}" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl index e25a370c049..e1f7ec2e23d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl @@ -15,4 +15,5 @@ void set_qt_environment() qputenv("QT_ENABLE_HIGHDPI_SCALING", "0"); qputenv("QT_LOGGING_RULES", "qt.qml.connections=false"); qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2.conf"); + qputenv("QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT", "1"); } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index ab1830752b1..851e3780899 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -259,6 +259,13 @@ "({ 'TargetQuickVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index 1a9e07cb63c..37e40ff8baf 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -18,6 +18,8 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "UIClassName", "value": "Screen01" }, + { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, @@ -216,6 +218,13 @@ "({ 'TargetQuickVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index 2e07c4725a1..642cd7a8e46 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -216,6 +216,13 @@ "({ 'TargetQuickVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index 422db173497..a2ece501ad2 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -216,6 +216,13 @@ "({ 'TargetQuickVersion': '6.3' })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4' + })" } ] } diff --git a/share/qtcreator/templates/wizards/files/effect/file.qep b/share/qtcreator/templates/wizards/files/effect/file.qep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/share/qtcreator/templates/wizards/files/effect/wizard.json b/share/qtcreator/templates/wizards/files/effect/wizard.json new file mode 100644 index 00000000000..d966f097eda --- /dev/null +++ b/share/qtcreator/templates/wizards/files/effect/wizard.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "supportedProjectTypes": [ ], + "id": "J.QEP", + "category": "U.QEP", + "trDescription": "Creates an Effect Maker file.", + "trDisplayName": "Effect File (Effect Maker)", + "trDisplayCategory": "Effects", + "iconText": "qep", + "platformIndependent": true, + "enabled": true, + + "options": { "key": "DefaultSuffix", "value": "qep" }, + + "pages" : + [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators" : + [ + { + "typeId": "File", + "data": + { + "source": "file.qep", + "target": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}", + "openInEditor": false + } + } + ] +} diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 70e8ec4c8ad..d3fedd7b055 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -181,7 +181,8 @@ bool AndroidDeployQtStep::init() } const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit()); - if (qt && qt->supportsMultipleQtAbis() && !selectedAbis.contains(info.cpuAbi.first())) { + if (qt && qt->supportsMultipleQtAbis() && !info.cpuAbi.isEmpty() && + !selectedAbis.contains(info.cpuAbi.first())) { TaskHub::addTask(DeploymentTask(Task::Warning, Tr::tr("Android: The main ABI of the deployment device (%1) is not selected. The app " "execution or debugging might not work properly. Add it from Projects > Build > " diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 971c701d5d8..f297a359db1 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -68,7 +68,7 @@ void DocumentLocatorFilter::updateSymbols(const DocumentUri &uri, return; QMutexLocker locker(&m_mutex); m_currentSymbols = symbols; - emit symbolsUpToDate({}); + emit symbolsUpToDate(QPrivateSignal()); } void DocumentLocatorFilter::resetSymbols() diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index b0984199aea..efd14b453b0 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -7,6 +7,7 @@ QtcPlugin { Depends { name: "Qt.widgets" } Depends { name: "Qt.testlib"; condition: qtc.testsEnabled } Depends { name: "Utils" } + Depends { name: "app_version_header" } Depends { name: "Core" } Depends { name: "BareMetal" } diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 1f665cb0b47..153e409c450 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -208,7 +208,7 @@ QObject *AssetsLibraryModel::rootDir() const bool AssetsLibraryModel::isEmpty() const { return m_isEmpty; -}; +} void AssetsLibraryModel::setIsEmpty(bool empty) { @@ -216,7 +216,7 @@ void AssetsLibraryModel::setIsEmpty(bool empty) m_isEmpty = empty; emit isEmptyChanged(); } -}; +} QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const { @@ -374,6 +374,13 @@ const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() return retList; } +const QStringList &AssetsLibraryModel::supportedEffectMakerSuffixes() +{ + // These are file types only supported by Effect Maker + static QStringList retList {"*.qep"}; + return retList; +} + const QSet &AssetsLibraryModel::supportedSuffixes() { static QSet allSuffixes; @@ -388,6 +395,7 @@ const QSet &AssetsLibraryModel::supportedSuffixes() insertSuffixes(supportedAudioSuffixes()); insertSuffixes(supportedVideoSuffixes()); insertSuffixes(supportedTexture3DSuffixes()); + insertSuffixes(supportedEffectMakerSuffixes()); } return allSuffixes; } diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 233347f0afc..82dd1568d57 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -44,6 +44,7 @@ public: static const QStringList &supportedAudioSuffixes(); static const QStringList &supportedVideoSuffixes(); static const QStringList &supportedTexture3DSuffixes(); + static const QStringList &supportedEffectMakerSuffixes(); static const QSet &supportedSuffixes(); const QSet &previewableSuffixes() const; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 29fd84b2073..44f1c2e747b 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -22,11 +22,18 @@ #include #include #include +#include "utils/environment.h" +#include "utils/filepath.h" +#include "utils/qtcprocess.h" #include #include #include +#include +#include +#include + #include #include #include @@ -43,6 +50,9 @@ #include #include +#include +#include + namespace QmlDesigner { static QString propertyEditorResourcesPath() @@ -126,6 +136,11 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon m_assetCompressionTimer.start(); }); + connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, + [](const QString &changeFilePath) { + QmlDesignerPlugin::instance()->emitAssetChanged(changeFilePath); + }); + auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); @@ -225,6 +240,48 @@ QSet AssetsLibraryWidget::supportedAssetSuffixes(bool complex) return suffixes; } +void AssetsLibraryWidget::openEffectMaker(const QString &filePath) +{ + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + qWarning() << __FUNCTION__ << "No project open"; + return; + } + + Utils::FilePath projectPath = target->project()->projectDirectory(); + QString effectName = QFileInfo(filePath).baseName(); + QString effectResDir = "asset_imports/Effects/" + effectName; + Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir); + if (!effectResPath.exists()) + QDir(projectPath.toString()).mkpath(effectResDir); + + const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + if (baseQtVersion) { + auto effectMakerPath = baseQtVersion->binPath().pathAppended("QQEffectMaker").withExecutableSuffix(); + if (!effectMakerPath.exists()) { + qWarning() << __FUNCTION__ << "Cannot find EffectMaker app"; + return; + } + + Utils::FilePath effectPath = Utils::FilePath::fromString(filePath); + QString effectContents = QString::fromUtf8(effectPath.fileContents().value_or(QByteArray())); + QStringList arguments; + arguments << filePath; + if (effectContents.isEmpty()) + arguments << "--create"; + arguments << "--exportpath" << effectResPath.toString(); + + Utils::Environment env = Utils::Environment::systemEnvironment(); + if (env.osType() == Utils::OsTypeMac) + env.appendOrSet("QSG_RHI_BACKEND", "metal"); + + m_qqemProcess.reset(new Utils::QtcProcess); + m_qqemProcess->setEnvironment(env); + m_qqemProcess->setCommand({ effectMakerPath, arguments }); + m_qqemProcess->start(); + } +} + void AssetsLibraryWidget::setModel(Model *model) { m_model = model; @@ -296,6 +353,9 @@ QPair AssetsLibraryWidget::getAssetTypeAndData(const QStrin } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { // Data: Image format (suffix) return {Constants::MIME_TYPE_ASSET_TEXTURE3D, suffix.toUtf8()}; + } else if (AssetsLibraryModel::supportedEffectMakerSuffixes().contains(suffix)) { + // Data: Effect Maker format (suffix) + return {Constants::MIME_TYPE_ASSET_EFFECT, suffix.toUtf8()}; } } return {}; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index d78345460f6..dad53e4e731 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -20,7 +20,10 @@ QT_BEGIN_NAMESPACE class QShortcut; QT_END_NAMESPACE -namespace Utils { class FileSystemWatcher; } +namespace Utils { + class FileSystemWatcher; + class QtcProcess; +} namespace QmlDesigner { @@ -61,6 +64,7 @@ public: const QList &complexFilePaths, const QString &targetDirPath = {}); Q_INVOKABLE QSet supportedAssetSuffixes(bool complex); + Q_INVOKABLE void openEffectMaker(const QString &filePath); signals: void itemActivated(const QString &itemName); @@ -92,6 +96,8 @@ private: bool m_updateRetry = false; QString m_filterText; QPoint m_dragStartPoint; + + std::unique_ptr m_qqemProcess; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 1d09f30d182..ba2e439fca5 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -293,7 +293,8 @@ void ActionEditor::updateWindowName(const QString &targetName) } void ActionEditor::invokeEditor(SignalHandlerProperty signalHandler, - std::function onReject, + std::function removeSignalFunction, + bool removeOnReject, QObject * parent) { if (!signalHandler.isValid()) @@ -324,15 +325,23 @@ void ActionEditor::invokeEditor(SignalHandlerProperty signalHandler, if (!editor) return; if (editor->m_modelNode.isValid()) { - editor->m_modelNode.view()->executeInTransaction("ActionEditor::" - "invokeEditorAccepted", - [=]() { - editor->m_modelNode - .signalHandlerProperty( - signalHandler.name()) - .setSource( - editor->connectionValue()); - }); + editor->m_modelNode.view() + ->executeInTransaction("ActionEditor::" + "invokeEditorAccepted", + [=]() { + if (!editor) + return; + + const QString newSource = editor->connectionValue(); + if ((newSource.isNull() || newSource.trimmed().isEmpty()) + && removeSignalFunction) { + removeSignalFunction(signalHandler); + } else { + editor->m_modelNode + .signalHandlerProperty(signalHandler.name()) + .setSource(newSource); + } + }); } //closing editor widget somewhy triggers rejected() signal. Lets disconect before it affects us: @@ -344,10 +353,10 @@ void ActionEditor::invokeEditor(SignalHandlerProperty signalHandler, if (!editor) return; - if (onReject) { + if (removeOnReject && removeSignalFunction) { editor->m_modelNode.view()->executeInTransaction("ActionEditor::" "invokeEditorOnRejectFunc", - [=]() { onReject(signalHandler); }); + [=]() { removeSignalFunction(signalHandler); }); } //closing editor widget somewhy triggers rejected() signal 2nd time. Lets disconect before it affects us: diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index 3cab2cef634..32f73e9b4b5 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -46,7 +46,8 @@ public: Q_INVOKABLE void updateWindowName(const QString &targetName = {}); static void invokeEditor(SignalHandlerProperty signalHandler, - std::function onReject = nullptr, + std::function removeSignalFunction = nullptr, + bool removeOnReject = false, QObject *parent = nullptr); signals: diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index c876ea170d3..090d4740fe3 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -211,8 +211,8 @@ const char addShadersDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResource const char add3DAssetsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "3D Assets"); const char addQt3DSPresentationsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Qt 3D Studio Presentations"); - -const char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Add Custom Effect"); +const char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", + "Effect Maker Files"); } //ComponentCoreConstants diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 105c7c09198..4f02a0076cb 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -549,6 +549,8 @@ ModelNode createNewConnection(ModelNode targetNode) void removeSignal(SignalHandlerProperty signalHandler) { + if (!signalHandler.isValid()) + return; auto connectionNode = signalHandler.parentModelNode(); auto connectionSignals = connectionNode.signalProperties(); if (connectionSignals.size() > 1) { @@ -662,10 +664,14 @@ public: QString( QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), [=](const SelectionContext &) { - signalHandler.parentModelNode().view()->executeInTransaction( - "ConnectionsModelNodeActionGroup::" - "openConnectionsEditor", - [signalHandler]() { ActionEditor::invokeEditor(signalHandler); }); + signalHandler.parentModelNode() + .view() + ->executeInTransaction("ConnectionsModelNodeActionGroup::" + "openConnectionsEditor", + [signalHandler]() { + ActionEditor::invokeEditor(signalHandler, + removeSignal); + }); }); activeSignalHandlerGroup->addAction(openEditorAction); @@ -728,7 +734,7 @@ public: newHandler.setSource( QString("console.log(\"%1.%2\")").arg(currentNode.id(), signalStr)); - ActionEditor::invokeEditor(newHandler, removeSignal); + ActionEditor::invokeEditor(newHandler, removeSignal, true); }); }); newSignal->addAction(openEditorAction); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index d21d6abcc46..a1dd5914a23 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1607,10 +1607,23 @@ void updateImported3DAsset(const SelectionContext &selectionContext) if (selectionContext.view()) { selectionContext.view()->emitCustomNotification( "UpdateImported3DAsset", {selectionContext.currentSingleSelectedNode()}); - } } +Utils::FilePath getEffectsDirectory() +{ + QString defaultDir = "asset_imports/Effects"; + Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); + Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir); + + if (!effectsPath.exists()) { + QDir dir(projectPath.toString()); + dir.mkpath(defaultDir); + } + + return effectsPath; +} + } // namespace ModelNodeOperations } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 27a68d55236..9238e1a81e0 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -5,6 +5,8 @@ #include "selectioncontext.h" +#include + namespace QmlDesigner { enum class AddFilesResult { Succeeded, Failed, Cancelled }; @@ -55,7 +57,7 @@ void addItemToStackedContainer(const SelectionContext &selectionContext); void increaseIndexOfStackedContainer(const SelectionContext &selectionContext); void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext); void addTabBarToStackedContainer(const SelectionContext &selectionContext); -AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory); +QMLDESIGNERCORE_EXPORT AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory); AddFilesResult addImageToProject(const QStringList &fileNames, const QString &directory); AddFilesResult addFontToProject(const QStringList &fileNames, const QString &directory); AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &directory); @@ -76,6 +78,8 @@ void addMouseAreaFill(const SelectionContext &selectionContext); void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); +QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsDirectory(); + // ModelNodePreviewImageOperations QVariant previewImageDataForGenericNode(const ModelNode &modelNode); QVariant previewImageDataForImageNode(const ModelNode &modelNode); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index eb922fb6d95..e9b751e49d7 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -57,8 +57,10 @@ public: centerHorizontal, centerVertical, closeCross, + closeLink, colorPopupClose, columnsAndRows, + copyLink, copyStyle, cornerA, cornerB, @@ -101,7 +103,6 @@ public: gridView, idAliasOff, idAliasOn, - imported, infinity, keyframe, linkTriangle, @@ -115,6 +116,7 @@ public: minus, mirror, newMaterial, + openLink, openMaterialBrowser, orientation, paddingEdge, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 22cfe0d8db3..380f91a80cd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -226,7 +226,7 @@ void Edit3DWidget::onCreateAction() // QString::number(entry.majorVersion()) // + QLatin1Char('.') // + QString::number(entry.minorVersion())); - // if (!m_view->model()->hasImport(import)) + // if (!m_view->model()->hasImport(import, true, true)) // m_view->model()->changeImports({import}, {}); // int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt(); diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 661e54fb58a..81920ca287c 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -220,7 +220,8 @@ void AbstractFormEditorTool::dragEnterEvent(const QList &itemLis for (const QString &assetPath : assetPaths) { QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; if (assetType == Constants::MIME_TYPE_ASSET_IMAGE - || assetType == Constants::MIME_TYPE_ASSET_FONT) { + || assetType == Constants::MIME_TYPE_ASSET_FONT + || assetType == Constants::MIME_TYPE_ASSET_EFFECT) { hasValidAssets = true; break; } diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 7377cbe6c1a..735a88f7d7d 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -218,38 +218,64 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData) return mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); } -void DragTool::dropEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) +void DragTool::dropEvent(const QList &itemList, QGraphicsSceneDragDropEvent *event) { if (canBeDropped(event->mimeData())) { event->accept(); end(generateUseSnapping(event->modifiers())); + QString effectPath; + const QStringList assetPaths = QString::fromUtf8(event->mimeData() + ->data(Constants::MIME_TYPE_ASSETS)).split(','); + for (auto &path : assetPaths) { + auto assetType = AssetsLibraryWidget::getAssetTypeAndData(path).first; + if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { + effectPath = path; + break; + } + } + bool resetPuppet = false; - for (auto &node : m_dragNodes) { - if (node.isValid()) { - if ((node.instanceParentItem().isValid() - && node.instanceParent().modelNode().metaInfo().isLayoutable()) - || node.isFlowItem()) { - node.removeProperty("x"); - node.removeProperty("y"); - resetPuppet = true; + + if (!effectPath.isEmpty()) { + FormEditorItem *targetContainerFormEditorItem = targetContainerOrRootItem(itemList); + if (targetContainerFormEditorItem) { + QmlItemNode parentQmlItemNode = targetContainerFormEditorItem->qmlItemNode(); + QString effectName = QFileInfo(effectPath).baseName(); + QmlItemNode effectNode = QmlItemNode::createQmlItemNodeForEffect(view(), parentQmlItemNode, effectName); + + view()->setSelectedModelNodes({effectNode}); + view()->resetPuppet(); + + commitTransaction(); + } + } else { + for (QmlItemNode &node : m_dragNodes) { + if (node.isValid()) { + if ((node.instanceParentItem().isValid() + && node.instanceParent().modelNode().metaInfo().isLayoutable()) + || node.isFlowItem()) { + node.removeProperty("x"); + node.removeProperty("y"); + resetPuppet = true; + } } } - } - if (resetPuppet) - view()->resetPuppet(); // Otherwise the layout might not reposition the items + if (resetPuppet) + view()->resetPuppet(); // Otherwise the layout might not reposition the items - commitTransaction(); + commitTransaction(); - if (!m_dragNodes.isEmpty()) { - QList nodeList; - for (auto &node : std::as_const(m_dragNodes)) { - if (node.isValid()) - nodeList.append(node); + if (!m_dragNodes.isEmpty()) { + QList nodeList; + for (auto &node : std::as_const(m_dragNodes)) { + if (node.isValid()) + nodeList.append(node); + } + view()->setSelectedModelNodes(nodeList); } - view()->setSelectedModelNodes(nodeList); + m_dragNodes.clear(); } - m_dragNodes.clear(); view()->changeToSelectionTool(); } @@ -325,10 +351,17 @@ void DragTool::createDragNodes(const QMimeData *mimeData, const QPointF &scenePo void DragTool::dragMoveEvent(const QList &itemList, QGraphicsSceneDragDropEvent *event) { - if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData())) { + FormEditorItem *targetContainerItem = targetContainerOrRootItem(itemList); + const QStringList assetPaths = QString::fromUtf8(event->mimeData() + ->data(Constants::MIME_TYPE_ASSETS)).split(','); + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first; + + if (!m_blockMove + && !m_isAborted + && canBeDropped(event->mimeData()) + && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { event->accept(); if (!m_dragNodes.isEmpty()) { - FormEditorItem *targetContainerItem = targetContainerOrRootItem(itemList); if (targetContainerItem) { move(event->scenePos(), itemList); } else { @@ -342,7 +375,7 @@ void DragTool::dragMoveEvent(const QList &itemList, QGraphicsSc } else { createDragNodes(event->mimeData(), event->scenePos(), itemList); } - } else { + } else if (assetType != Constants::MIME_TYPE_ASSET_EFFECT) { event->ignore(); } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp index 716fb57dbf5..60910abb046 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp @@ -72,6 +72,11 @@ bool BundleMaterialCategory::visible() const return m_visible; } +bool BundleMaterialCategory::expanded() const +{ + return m_expanded; +} + QList BundleMaterialCategory::categoryMaterials() const { return m_categoryMaterials; diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h index 2336a03d010..14f7ddf654e 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h @@ -35,8 +35,11 @@ class BundleMaterialCategory : public QObject { Q_OBJECT - Q_PROPERTY(QString categoryName MEMBER m_name CONSTANT) - Q_PROPERTY(bool categoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged) + Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT) + Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged) + Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged) + Q_PROPERTY(QList bundleCategoryMaterials MEMBER m_categoryMaterials + NOTIFY bundleMaterialsModelChanged) public: BundleMaterialCategory(QObject *parent, const QString &name); @@ -47,14 +50,18 @@ public: QString name() const; bool visible() const; + bool expanded() const; QList categoryMaterials() const; signals: void categoryVisibleChanged(); + void categoryExpandChanged(); + void bundleMaterialsModelChanged(); private: QString m_name; bool m_visible = true; + bool m_expanded = true; QList m_categoryMaterials; }; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp index a43a23a1d1d..7cb2a39c0d1 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp @@ -54,17 +54,26 @@ QVariant MaterialBrowserBundleModel::data(const QModelIndex &index, int role) co QTC_ASSERT(index.isValid() && index.row() < m_bundleCategories.count(), return {}); QTC_ASSERT(roleNames().contains(role), return {}); + return m_bundleCategories.at(index.row())->property(roleNames().value(role)); +} + +bool MaterialBrowserBundleModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || !roleNames().contains(role)) + return false; + QByteArray roleName = roleNames().value(role); - if (roleName == "bundleCategory") - return m_bundleCategories.at(index.row())->name(); + BundleMaterialCategory *bundleCategory = m_bundleCategories.at(index.row()); + QVariant currValue = bundleCategory->property(roleName); - if (roleName == "bundleCategoryVisible") - return m_bundleCategories.at(index.row())->visible(); + if (currValue != value) { + bundleCategory->setProperty(roleName, value); - if (roleName == "bundleMaterialsModel") - return QVariant::fromValue(m_bundleCategories.at(index.row())->categoryMaterials()); + emit dataChanged(index, index, {role}); + return true; + } - return {}; + return false; } bool MaterialBrowserBundleModel::isValidIndex(int idx) const @@ -75,16 +84,17 @@ bool MaterialBrowserBundleModel::isValidIndex(int idx) const QHash MaterialBrowserBundleModel::roleNames() const { static const QHash roles { - {Qt::UserRole + 1, "bundleCategory"}, + {Qt::UserRole + 1, "bundleCategoryName"}, {Qt::UserRole + 2, "bundleCategoryVisible"}, - {Qt::UserRole + 3, "bundleMaterialsModel"} + {Qt::UserRole + 3, "bundleCategoryExpanded"}, + {Qt::UserRole + 4, "bundleCategoryMaterials"} }; return roles; } void MaterialBrowserBundleModel::loadMaterialBundle() { - if (m_matBundleExists || m_probeMatBundleDir) + if (m_matBundleLoaded || m_probeMatBundleDir) return; QDir matBundleDir(qEnvironmentVariable("MATERIAL_BUNDLE_PATH")); @@ -120,7 +130,7 @@ void MaterialBrowserBundleModel::loadMaterialBundle() } } - m_matBundleExists = true; + m_matBundleLoaded = true; QString bundleId = m_matBundleObj.value("id").toString(); @@ -174,20 +184,6 @@ void MaterialBrowserBundleModel::loadMaterialBundle() }); } -bool MaterialBrowserBundleModel::hasQuick3DImport() const -{ - return m_hasQuick3DImport; -} - -void MaterialBrowserBundleModel::setHasQuick3DImport(bool b) -{ - if (b == m_hasQuick3DImport) - return; - - m_hasQuick3DImport = b; - emit hasQuick3DImportChanged(); -} - bool MaterialBrowserBundleModel::hasMaterialRoot() const { return m_hasMaterialRoot; @@ -202,6 +198,11 @@ void MaterialBrowserBundleModel::setHasMaterialRoot(bool b) emit hasMaterialRootChanged(); } +bool MaterialBrowserBundleModel::matBundleExists() const +{ + return m_matBundleLoaded && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3; +} + Internal::BundleImporter *MaterialBrowserBundleModel::bundleImporter() const { return m_importer; @@ -243,6 +244,17 @@ void MaterialBrowserBundleModel::updateImportedState(const QStringList &imported resetModel(); } +void MaterialBrowserBundleModel::setQuick3DImportVersion(int major, int minor) +{ + bool bundleExisted = matBundleExists(); + + m_quick3dMajorVersion = major; + m_quick3dMinorVersion = minor; + + if (bundleExisted != matBundleExists()) + emit matBundleExistsChanged(); +} + void MaterialBrowserBundleModel::resetModel() { beginResetModel(); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h index 8197ebd78b0..beb445422e7 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h @@ -46,9 +46,8 @@ class MaterialBrowserBundleModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(bool matBundleExists MEMBER m_matBundleExists CONSTANT) + Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged) Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) @@ -57,17 +56,19 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; QHash roleNames() const override; void setSearchText(const QString &searchText); void updateImportedState(const QStringList &importedMats); - bool hasQuick3DImport() const; - void setHasQuick3DImport(bool b); + void setQuick3DImportVersion(int major, int minor); bool hasMaterialRoot() const; void setHasMaterialRoot(bool b); + bool matBundleExists() const; + Internal::BundleImporter *bundleImporter() const; void resetModel(); @@ -86,6 +87,7 @@ signals: void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); void importerRunningChanged(); + void matBundleExistsChanged(); private: void loadMaterialBundle(); @@ -97,11 +99,13 @@ private: Internal::BundleImporter *m_importer = nullptr; bool m_isEmpty = true; - bool m_hasQuick3DImport = false; bool m_hasMaterialRoot = false; - bool m_matBundleExists = false; + bool m_matBundleLoaded = false; bool m_probeMatBundleDir = false; bool m_importerRunning = false; + + int m_quick3dMajorVersion = -1; + int m_quick3dMinorVersion = -1; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index 58c867782dc..ea2e1e47089 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -83,41 +83,54 @@ bool MaterialBrowserModel::loadPropertyGroups(const QString &path) { bool ok = true; - if (m_propertyGroupsObj.isEmpty()) { - QFile matPropsFile(path); + QFile matPropsFile(path); + if (!matPropsFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open propertyGroups.json"); + ok = false; + } - if (!matPropsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open propertyGroups.json"); + if (ok) { + QJsonDocument matPropsJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); + if (matPropsJsonDoc.isNull()) { + qWarning("Invalid propertyGroups.json file"); ok = false; - } - - if (ok) { - QJsonDocument matPropsJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); - if (matPropsJsonDoc.isNull()) { - qWarning("Invalid propertyGroups.json file"); - ok = false; - } else { - m_propertyGroupsObj = matPropsJsonDoc.object(); - } + } else { + m_propertyGroupsObj = matPropsJsonDoc.object(); } } m_defaultMaterialSections.clear(); m_principledMaterialSections.clear(); + m_specularGlossyMaterialSections.clear(); m_customMaterialSections.clear(); if (ok) { m_defaultMaterialSections.append(m_propertyGroupsObj.value("DefaultMaterial").toObject().keys()); m_principledMaterialSections.append(m_propertyGroupsObj.value("PrincipledMaterial").toObject().keys()); + m_specularGlossyMaterialSections.append(m_propertyGroupsObj.value("SpecularGlossyMaterial").toObject().keys()); QStringList customMatSections = m_propertyGroupsObj.value("CustomMaterial").toObject().keys(); if (customMatSections.size() > 1) // as of now custom material has only 1 section, so we don't add it m_customMaterialSections.append(customMatSections); + } else { + m_propertyGroupsObj = {}; } emit materialSectionsChanged(); return ok; } +void MaterialBrowserModel::unloadPropertyGroups() +{ + if (!m_propertyGroupsObj.isEmpty()) { + m_propertyGroupsObj = {}; + m_defaultMaterialSections.clear(); + m_principledMaterialSections.clear(); + m_specularGlossyMaterialSections.clear(); + m_customMaterialSections.clear(); + emit materialSectionsChanged(); + } +} + QHash MaterialBrowserModel::roleNames() const { static const QHash roles { @@ -381,6 +394,7 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io } } validProps.remove("objectName"); + validProps.remove("data"); if (m_allPropsCopied || dynamicPropsCopied || m_propertyGroupsObj.empty()) { copiedProps = validProps.values(); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 2e4a4fc5cba..f850aaedb01 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -25,6 +25,7 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(QString copiedMaterialType READ copiedMaterialType WRITE setCopiedMaterialType NOTIFY copiedMaterialTypeChanged) Q_PROPERTY(QStringList defaultMaterialSections MEMBER m_defaultMaterialSections NOTIFY materialSectionsChanged) Q_PROPERTY(QStringList principledMaterialSections MEMBER m_principledMaterialSections NOTIFY materialSectionsChanged) + Q_PROPERTY(QStringList specularGlossyMaterialSections MEMBER m_specularGlossyMaterialSections NOTIFY materialSectionsChanged) Q_PROPERTY(QStringList customMaterialSections MEMBER m_customMaterialSections NOTIFY materialSectionsChanged) public: @@ -58,6 +59,7 @@ public: int materialIndex(const ModelNode &material) const; ModelNode materialAt(int idx) const; bool loadPropertyGroups(const QString &path); + void unloadPropertyGroups(); void resetModel(); @@ -105,6 +107,7 @@ private: QString m_searchText; QList m_materialList; QStringList m_defaultMaterialSections; + QStringList m_specularGlossyMaterialSections; QStringList m_principledMaterialSections; QStringList m_customMaterialSections; ModelNode m_copiedMaterial; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 28a37a5be82..6b3c3baed83 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -22,6 +22,14 @@ #include #include +#ifndef QMLDESIGNER_TEST +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -100,7 +108,7 @@ WidgetInfo MaterialBrowserView::widgetInfo() } } for (const PropertyName &propName : std::as_const(propNames)) { - if (propName != "objectName") + if (propName != "objectName" && propName != "data") mat.removeProperty(propName); } } @@ -267,11 +275,12 @@ void MaterialBrowserView::modelAttached(Model *model) rootModelNode().metaInfo().isQtQuick3DMaterial()); m_hasQuick3DImport = model->hasImport("QtQuick3D"); + updateBundleMaterialsQuick3DVersion(); updateBundleMaterialsImportedState(); // Project load is already very busy and may even trigger puppet reset, so let's wait a moment // before refreshing the model - QTimer::singleShot(1000, this, [this]() { + QTimer::singleShot(1000, model, [this]() { refreshModel(true); loadPropertyGroups(); // Needs the delay because it uses metaInfo }); @@ -279,7 +288,7 @@ void MaterialBrowserView::modelAttached(Model *model) void MaterialBrowserView::refreshModel(bool updateImages) { - if (!model() || !model()->nodeInstanceView()) + if (!model()) return; ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); @@ -311,6 +320,11 @@ void MaterialBrowserView::modelAboutToBeDetached(Model *model) { m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport); + if (m_propertyGroupsLoaded) { + m_propertyGroupsLoaded = false; + m_widget->materialBrowserModel()->unloadPropertyGroups(); + } + AbstractView::modelAboutToBeDetached(model); } @@ -416,7 +430,7 @@ void MaterialBrowserView::nodeRemoved([[maybe_unused]] const ModelNode &removedN void QmlDesigner::MaterialBrowserView::loadPropertyGroups() { - if (!m_hasQuick3DImport || m_propertyGroupsLoaded) + if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model()) return; QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath() @@ -443,6 +457,41 @@ void MaterialBrowserView::updateBundleMaterialsImportedState() m_widget->materialBrowserBundleModel()->updateImportedState(importedBundleMats); } +void MaterialBrowserView::updateBundleMaterialsQuick3DVersion() +{ + bool hasImport = false; + int major = -1; + int minor = -1; + const QString url {"QtQuick3D"}; + const auto imports = model()->imports(); + for (const auto &import : imports) { + if (import.url() == url) { + hasImport = true; + const int importMajor = import.majorVersion(); + if (major < importMajor) { + minor = -1; + major = importMajor; + } + if (major == importMajor) + minor = qMax(minor, import.minorVersion()); + } + } +#ifndef QMLDESIGNER_TEST + if (hasImport && major == -1) { + // Import without specifying version, so we take the kit version + auto target = ProjectExplorer::SessionManager::startupTarget(); + if (target) { + QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + if (qtVersion) { + major = qtVersion->qtVersion().majorVersion(); + minor = qtVersion->qtVersion().minorVersion(); + } + } + } +#endif + m_widget->materialBrowserBundleModel()->setQuick3DImportVersion(major, minor); +} + ModelNode MaterialBrowserView::getBundleMaterialDefaultInstance(const TypeName &type) { const QList materials = m_widget->materialBrowserModel()->materials(); @@ -470,6 +519,8 @@ void MaterialBrowserView::importsChanged([[maybe_unused]] const QList &a { bool hasQuick3DImport = model()->hasImport("QtQuick3D"); + updateBundleMaterialsQuick3DVersion(); + if (hasQuick3DImport == m_hasQuick3DImport) return; @@ -494,7 +545,7 @@ void MaterialBrowserView::customNotification(const AbstractView *view, if (idx != -1) m_widget->materialBrowserModel()->selectMaterial(idx); } else if (identifier == "refresh_material_browser") { - QTimer::singleShot(0, this, [this]() { + QTimer::singleShot(0, model(), [this]() { refreshModel(true); }); } else if (identifier == "delete_selected_material") { diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index 1d2322463bd..616a9a92666 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -47,6 +47,7 @@ private: bool isMaterial(const ModelNode &node) const; void loadPropertyGroups(); void updateBundleMaterialsImportedState(); + void updateBundleMaterialsQuick3DVersion(); void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {}); ModelNode getBundleMaterialDefaultInstance(const TypeName &type); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 599e4612391..d4c1cbf805f 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -59,9 +59,7 @@ MaterialEditorView::MaterialEditorView() m_ensureMatLibTimer.callOnTimeout([this] { if (model() && model()->rewriterView() && !model()->rewriterView()->hasIncompleteTypeInformation() && model()->rewriterView()->errors().isEmpty()) { - executeInTransaction("MaterialEditorView::MaterialEditorView", [this] { - ensureMaterialLibraryNode(); - }); + ensureMaterialLibraryNode(); m_ensureMatLibTimer.stop(); } }); @@ -714,8 +712,8 @@ void MaterialEditorView::updatePossibleTypes() return; // Ensure basic types are always first - static const QStringList basicTypes {"DefaultMaterial", "PrincipledMaterial", "CustomMaterial"}; - QStringList allTypes = basicTypes; + QStringList nonQuick3dTypes; + QStringList allTypes; const QList itemLibEntries = m_itemLibraryInfo->entries(); for (const ItemLibraryEntry &entry : itemLibEntries) { @@ -730,12 +728,22 @@ void MaterialEditorView::updatePossibleTypes() addImport = model()->hasImport(import, true, true); } if (addImport) { - QString typeName = QString::fromLatin1(entry.typeName().split('.').last()); - if (!allTypes.contains(typeName)) - allTypes.append(typeName); + const QList typeSplit = entry.typeName().split('.'); + const QString typeName = QString::fromLatin1(typeSplit.last()); + if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") { + if (!allTypes.contains(typeName)) + allTypes.append(typeName); + } else if (!nonQuick3dTypes.contains(typeName)) { + nonQuick3dTypes.append(typeName); + } } } } + + allTypes.sort(); + nonQuick3dTypes.sort(); + allTypes.append(nonQuick3dTypes); + m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); } @@ -1006,6 +1014,8 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) TypeName matType = material.type(); QmlObjectNode sourceMat(material); + ModelNode duplicateMatNode; + QList dynamicProps; executeInTransaction(__FUNCTION__, [&] { ModelNode matLib = materialLibraryNode(); @@ -1016,25 +1026,57 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) NodeMetaInfo metaInfo = model()->metaInfo(matType); QmlObjectNode duplicateMat = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); + duplicateMatNode = duplicateMat.modelNode(); + // set name and id QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy"; - duplicateMat.modelNode().variantProperty("objectName").setValue(newName); - duplicateMat.modelNode().setIdWithoutRefactoring(model()->generateIdFromName(newName, "material")); + duplicateMatNode.variantProperty("objectName").setValue(newName); + duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material")); - // sync properties + // sync properties. Only the base state is duplicated. const QList props = material.properties(); for (const AbstractProperty &prop : props) { - if (prop.name() == "objectName") + if (prop.name() == "objectName" || prop.name() == "data") continue; - if (prop.isVariantProperty()) - duplicateMat.setVariantProperty(prop.name(), prop.toVariantProperty().value()); - else if (prop.isBindingProperty()) - duplicateMat.setBindingProperty(prop.name(), prop.toBindingProperty().expression()); + if (prop.isVariantProperty()) { + if (prop.isDynamic()) { + dynamicProps.append(prop); + } else { + duplicateMatNode.variantProperty(prop.name()) + .setValue(prop.toVariantProperty().value()); + } + } else if (prop.isBindingProperty()) { + if (prop.isDynamic()) { + dynamicProps.append(prop); + } else { + duplicateMatNode.bindingProperty(prop.name()) + .setExpression(prop.toBindingProperty().expression()); + } + } } matLib.defaultNodeListProperty().reparentHere(duplicateMat); }); + + // For some reason, creating dynamic properties in the same transaction doesn't work, so + // let's do it in separate transaction. + // TODO: Fix the issue and merge transactions (QDS-8094) + if (!dynamicProps.isEmpty()) { + executeInTransaction(__FUNCTION__, [&] { + for (const AbstractProperty &prop : std::as_const(dynamicProps)) { + if (prop.isVariantProperty()) { + duplicateMatNode.variantProperty(prop.name()) + .setDynamicTypeNameAndValue(prop.dynamicTypeName(), + prop.toVariantProperty().value()); + } else if (prop.isBindingProperty()) { + duplicateMatNode.bindingProperty(prop.name()) + .setDynamicTypeNameAndExpression(prop.dynamicTypeName(), + prop.toBindingProperty().expression()); + } + } + }); + } } void MaterialEditorView::customNotification([[maybe_unused]] const AbstractView *view, diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 03586161a32..6a267f2742d 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -18,6 +18,7 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i // Texture // -> DefaultMaterial // -> PrincipledMaterial + // -> SpecularGlossyMaterial // -> SpriteParticle3D // -> TextureInput // -> SceneEnvironment @@ -40,7 +41,8 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i // -> Model if (insertInfo.isQtQuick3DTexture()) { - if (parentInfo.isQtQuick3DDefaultMaterial() || parentInfo.isQtQuick3DPrincipledMaterial()) { + if (parentInfo.isQtQuick3DDefaultMaterial() || parentInfo.isQtQuick3DPrincipledMaterial() + || parentInfo.isQtQuick3DSpecularGlossyMaterial()) { // All texture properties are valid targets for (const auto &property : parentInfo.properties()) { const auto &propType = property.propertyType(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 1c68168d010..12d496b2522 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -1024,7 +1024,8 @@ bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode, }; if (targetNode.metaInfo().isQtQuick3DDefaultMaterial() - || targetNode.metaInfo().isQtQuick3DPrincipledMaterial()) { + || targetNode.metaInfo().isQtQuick3DPrincipledMaterial() + || targetNode.metaInfo().isQtQuick3DSpecularGlossyMaterial()) { // if dropping an image on a material, create a texture instead of image // Show texture property selection dialog auto dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, diff --git a/src/plugins/qmldesigner/components/timelineeditor/easingcurvedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/easingcurvedialog.cpp index 5d3521b1675..e89f8520ea3 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/easingcurvedialog.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/easingcurvedialog.cpp @@ -69,6 +69,8 @@ EasingCurveDialog::EasingCurveDialog(const QList &frames, QWidget *pa presetBar->setDrawBase(false); presetBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + m_presets->initialize(presetBar); + auto *durationLabel = new QLabel("Duration (ms)"); auto *durationEdit = new QSpinBox; durationEdit->setMaximum(std::numeric_limits::max()); @@ -85,6 +87,8 @@ EasingCurveDialog::EasingCurveDialog(const QList &frames, QWidget *pa m_durationLayout->insertSpacing(4, hSpacing); m_durationLayout->addStretch(hSpacing); + m_splineEditor->setDuration(durationEdit->value()); + m_buttons->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); auto callButtonsClicked = [this](QAbstractButton *button) { buttonsClicked(m_buttons->standardButton(button)); @@ -122,9 +126,6 @@ EasingCurveDialog::EasingCurveDialog(const QList &frames, QWidget *pa connect(durationEdit, &QSpinBox::valueChanged, m_splineEditor, &SplineEditor::setDuration); connect(animateButton, &QPushButton::clicked, m_splineEditor, &SplineEditor::animate); - m_presets->initialize(presetBar); - - m_splineEditor->setDuration(durationEdit->value()); resize(QSize(1421, 918)); } diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp index d8f45b06795..f824c7fb038 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp @@ -33,6 +33,19 @@ constexpr int spacingg = 5; const QColor background = Qt::white; + +QString makeNameUnique(const QString& name, const QStringList& currentNames) +{ + QString n = name; + int idx = 0; + while (true) { + if (!currentNames.contains(n)) + return n; + n = name + "_" + QString::number(idx++); + } + return {}; +} + PresetItemDelegate::PresetItemDelegate(const QColor& background) : QStyledItemDelegate() , m_background(background) @@ -366,7 +379,7 @@ void PresetList::createItem() { EasingCurve curve; curve.makeDefault(); - createItem(createUniqueName(), curve); + createItem(makeNameUnique("Default", allNames()), curve); } void PresetList::createItem(const QString &name, const EasingCurve &curve) @@ -402,27 +415,6 @@ void PresetList::setItemData(const QModelIndex &index, const QVariant &curve, co } } -QString PresetList::createUniqueName() const -{ - QStringList names = allNames(); - auto nameIsUnique = [&](const QString &name) { - auto iter = std::find(names.begin(), names.end(), name); - if (iter == names.end()) - return true; - else - return false; - }; - - int counter = 0; - QString tmp("Default"); - QString name = tmp; - - while (!nameIsUnique(name)) - name = tmp + QString(" %1").arg(counter++); - - return name; -} - QStringList PresetList::allNames() const { QStringList names; @@ -529,7 +521,8 @@ bool PresetEditor::writePresets(const EasingCurve &curve) if (ok && !name.isEmpty()) { activate(m_customs->index()); - m_customs->createItem(name, curve); + QString uname = makeNameUnique(name, m_customs->allNames()); + m_customs->createItem(uname, curve); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.h b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.h index 09bc46a2c5e..323748a0dba 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.h +++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.h @@ -16,6 +16,8 @@ namespace QmlDesigner { class EasingCurve; class NamedEasingCurve; +QString makeNameUnique(const QString& name, const QStringList& currentNames); + class PresetItemDelegate : public QStyledItemDelegate { Q_OBJECT @@ -65,6 +67,8 @@ public: QColor curveColor() const; + QStringList allNames() const; + void initialize(int index); void readPresets(); @@ -89,12 +93,8 @@ protected: const QVector &roles = QVector()) override; private: - QStringList allNames() const; - QList storedCurves() const; - QString createUniqueName() const; - void removeSelectedItem(); private: diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index 52d286c053b..0f53aa0c7c2 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -39,7 +39,9 @@ public: bool isSameModule(const Import &other) const; int majorVersion() const; + int minorVersion() const; static int majorFromVersion(const QString &version); + static int minorFromVersion(const QString &version); private: Import(const QString &url, const QString &file, const QString &version, const QString &alias, const QStringList &importPaths); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 9338a160a6f..5e5984bbc4b 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -135,6 +135,7 @@ public: bool isQtQuick3DParticles3DSpriteParticle3D() const; bool isQtQuick3DPass() const; bool isQtQuick3DPrincipledMaterial() const; + bool isQtQuick3DSpecularGlossyMaterial() const; bool isQtQuick3DSceneEnvironment() const; bool isQtQuick3DShader() const; bool isQtQuick3DTexture() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index fce9787f92f..ada5887621a 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -60,7 +60,9 @@ public: const QPointF &position, NodeAbstractProperty parentproperty, bool executeInTransaction = true); - + static QmlItemNode createQmlItemNodeForEffect(AbstractView *view, + const QmlItemNode &parentNode, + const QString &effectName); QList children() const; QList resources() const; QList allDirectSubNodes() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 610d8ebfb20..725aec0e656 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2734,6 +2734,16 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const } } +bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick3D.SpecularGlossyMaterial"); + } +} + bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index b1c6ef23607..7ca9742d340 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -811,47 +811,48 @@ void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, in m_model.data()->d->changeRootNodeType(type, majorVersion, minorVersion); } -// Creates material library if it doesn't exist and moves any existing materials into it -// This function should be called only from inside a transaction, as it potentially does many -// changes to model, or undo stack should be cleared after the call. +// Creates material library if it doesn't exist and moves any existing materials into it. void AbstractView::ensureMaterialLibraryNode() { ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); if (matLib.isValid() || rootModelNode().metaInfo().isQtQuick3DMaterial()) return; - // Create material library node - auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() ? model()->qtQuick3DNodeMetaInfo() - : model()->qtQuickItemMetaInfo(); - matLib = createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion()); + executeInTransaction(__FUNCTION__, [&] { + // Create material library node + auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() + ? model()->qtQuick3DNodeMetaInfo() + : model()->qtQuickItemMetaInfo(); + matLib = createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion()); - matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); - rootModelNode().defaultNodeListProperty().reparentHere(matLib); + matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); + rootModelNode().defaultNodeListProperty().reparentHere(matLib); + }); - const QList materials = rootModelNode().subModelNodesOfType( - model()->qtQuick3DMaterialMetaInfo()); - if (!materials.isEmpty()) { - // Move all materials to under material library node - for (const ModelNode &node : materials) { - // If material has no name, set name to id - QString matName = node.variantProperty("objectName").value().toString(); - if (matName.isEmpty()) { - VariantProperty objNameProp = node.variantProperty("objectName"); - objNameProp.setValue(node.id()); + // Do the material reparentings in different transaction to work around issue QDS-8094 + executeInTransaction(__FUNCTION__, [&] { + const QList materials = rootModelNode().subModelNodesOfType( + model()->qtQuick3DMaterialMetaInfo()); + if (!materials.isEmpty()) { + // Move all materials to under material library node + for (const ModelNode &node : materials) { + // If material has no name, set name to id + QString matName = node.variantProperty("objectName").value().toString(); + if (matName.isEmpty()) { + VariantProperty objNameProp = node.variantProperty("objectName"); + objNameProp.setValue(node.id()); + } + + matLib.defaultNodeListProperty().reparentHere(node); } - - matLib.defaultNodeListProperty().reparentHere(node); } - } + }); } -// Returns ModelNode for project's material library. -// Since this calls ensureMaterialLibraryNode(), it should only be called within a transaction. +// Returns ModelNode for project's material library if it exists. ModelNode AbstractView::materialLibraryNode() { - ensureMaterialLibraryNode(); - ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); - return matLib; + return modelNodeForId(Constants::MATERIAL_LIB_ID); } // Assigns given material to a 3D model. diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index e2d4ede193d..7a6d836d2a9 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -80,6 +80,11 @@ int Import::majorVersion() const return majorFromVersion(m_version); } +int Import::minorVersion() const +{ + return minorFromVersion(m_version); +} + int Import::majorFromVersion(const QString &version) { if (version.isEmpty()) @@ -87,6 +92,16 @@ int Import::majorFromVersion(const QString &version) return version.split('.').first().toInt(); } +int Import::minorFromVersion(const QString &version) +{ + if (version.isEmpty()) + return -1; + const QStringList parts = version.split('.'); + if (parts.size() < 2) + return -1; + return parts[1].toInt(); +} + size_t qHash(const Import &import) { return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index fb7cd479724..9901e1bfa10 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -197,7 +197,7 @@ public: const AuxiliaryDataKeyView &key, const QVariant &data); void removeAuxiliaryData(const InternalNodePointer &node, const AuxiliaryDataKeyView &key); - [[noreturn]] void resetModelByRewriter(const QString &description); + void resetModelByRewriter(const QString &description); // Imports: const QList &imports() const { return m_imports; } diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index e3770a3c42e..d42c8f08a0e 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -159,6 +159,33 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view, return newQmlItemNode; } +QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view, + const QmlItemNode &parentNode, + const QString &effectName) +{ + QmlItemNode newQmlItemNode; + + QmlDesigner::Import import = Import::createLibraryImport("Effects." + effectName, "1.0"); + try { + if (!view->model()->hasImport(import, true, true)) + view->model()->changeImports({import}, {}); + } catch (const Exception &e) { + QTC_ASSERT(false, return QmlItemNode()); + } + + TypeName type(effectName.toUtf8()); + newQmlItemNode = QmlItemNode(view->createModelNode(type, 1, 0)); + NodeAbstractProperty parentProperty = parentNode.defaultNodeAbstractProperty(); + parentProperty.reparentHere(newQmlItemNode); + + newQmlItemNode.modelNode().bindingProperty("source").setExpression("parent"); + newQmlItemNode.modelNode().bindingProperty("anchors.fill").setExpression("parent"); + + QTC_ASSERT(newQmlItemNode.isValid(), return QmlItemNode()); + + return newQmlItemNode; +} + bool QmlItemNode::isValid() const { return isValidQmlItemNode(modelNode()); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 51cfdfd6acd..5ecc3d530c3 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -70,7 +70,7 @@ QStringList supportedVersionsList() { static const QStringList list = {"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", - "2.14", "2.15", "6.0", "6.1", "6.2", "6.3"}; + "2.14", "2.15", "6.0", "6.1", "6.2", "6.3", "6.4"}; return list; } @@ -509,10 +509,6 @@ public: qDebug() << metaInfo.isValid() << metaInfo.typeName(); if (metaInfo.isValid()) qDebug() << metaInfo.superClasses().front().typeName(); - - if (!metaInfo.isFileComponent() && m_model == m_model->metaInfoProxyModel() - && metaInfo.isValid()) - throw RewritingException(__LINE__, __FUNCTION__, __FILE__, "test", "test"); } typeName = QString::fromUtf8(metaInfo.typeName()); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 23f14724159..eacb0578c02 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -66,6 +66,7 @@ inline constexpr char Picture[] = "Picture"; inline constexpr char Popup[] = "Popup"; inline constexpr char Positioner[] = "Positioner"; inline constexpr char PrincipledMaterial[] = "PrincipledMaterial"; +inline constexpr char SpecularGlossyMaterial[] = "SpecularGlossyMaterial"; inline constexpr char PropertyAnimation[] = "PropertyAnimation"; inline constexpr char PropertyChanges[] = "PropertyChanges"; inline constexpr char QML[] = "QML"; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index ee9870386bf..9b5dac8aa9f 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -83,6 +83,7 @@ const char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset const char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video"; const char MIME_TYPE_ASSET_TEXTURE3D[] = "application/vnd.qtdesignstudio.asset.texture3d"; const char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list"; +const char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect"; // Menus const char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 0decbcace86..99ff2b59c32 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -631,6 +631,11 @@ void QmlDesignerPlugin::emitCurrentTextEditorChanged(Core::IEditor *editor) d->blockEditorChange = false; } +void QmlDesignerPlugin::emitAssetChanged(const QString &assetPath) +{ + emit assetChanged(assetPath); +} + double QmlDesignerPlugin::formEditorDevicePixelRatio() { if (QmlDesignerPlugin::settings().value(DesignerSettingsKey::IGNORE_DEVICE_PIXEL_RATIO).toBool()) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index d34e0ae1c3c..49eaa5fcabd 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -63,6 +63,8 @@ public: void switchToTextModeDeferred(); void emitCurrentTextEditorChanged(Core::IEditor *editor); + void emitAssetChanged(const QString &assetPath); + static double formEditorDevicePixelRatio(); static void contextHelp(const Core::IContext::HelpCallback &callback, const QString &id); @@ -81,7 +83,7 @@ public: signals: void usageStatisticsNotifier(const QString &identifier); void usageStatisticsUsageTimer(const QString &identifier, int elapsed); - + void assetChanged(const QString &assetPath); private: // functions void integrateIntoQtCreator(QWidget *modeWidget); diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index c45db41b8d2..6420b398651 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -459,7 +459,7 @@ MetaInfo { } ItemLibraryEntry { - name: "ColorAnimation" + name: "Color Animation" category: "d.Qt Quick - Animation" libraryIcon: ":/qtquickplugin/images/item-icon.png" version: "2.0" diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 0786e2b5805..a64a3a888d9 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -66,6 +66,14 @@ static int preferedQtTarget(Target *target) return 5; } +static bool allowOnlySingleProject() +{ + QSettings *settings = Core::ICore::settings(); + const QString qdsAllowMultipleProjects = "QML/Designer/AllowMultipleProjects"; + + return !settings->value(qdsAllowMultipleProjects, false).toBool(); +} + Utils::FilePaths QmlProject::getUiQmlFilesForFolder(const Utils::FilePath &folder) { const Utils::FilePaths uiFiles = files([&](const ProjectExplorer::Node *node) { @@ -86,9 +94,10 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); }); if (QmlProject::isQtDesignStudio()) { - - EditorManager::closeAllDocuments(); - SessionManager::closeAllProjects(); + if (allowOnlySingleProject()) { + EditorManager::closeAllDocuments(); + SessionManager::closeAllProjects(); + } m_openFileConnection = connect(this,