diff options
author | Eike Ziller <eike.ziller@qt.io> | 2023-01-12 09:07:27 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2023-01-12 09:07:27 +0100 |
commit | 211d6507c338f63ebcf1ed58a3206c565bc6cb5c (patch) | |
tree | c1ba641cdb143b0f8696f847c7cc5834376e570c | |
parent | 69a79010d6eecb9411385ba4000dd0d088963c54 (diff) | |
parent | a61f8b02d30d2dfd8716e2ed7b7a23d287b68677 (diff) | |
download | qt-creator-211d6507c338f63ebcf1ed58a3206c565bc6cb5c.tar.gz |
Merge remote-tracking branch 'origin/qds/dev'
Conflicts:
src/plugins/updateinfo/updateinfoplugin.cpp
Change-Id: Ie1bf2ad434f0224fb91caf91b443daae3d5b5ec0
58 files changed, 1378 insertions, 312 deletions
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index 6faef0cfc8..f4728fa415 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc @@ -84,7 +84,7 @@ \list \li \uicontrol Component exports the layer as a separate UI file that contains all the exportable artwork and text in it. Only - Artboards can be exported as components. + Artboards and XD component layers can be exported as components. \li \uicontrol Child exports each asset of the selected group or layer a separate PNG file, with references to the images in the component file. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc index f2063e6a05..6d1fbc5bd1 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc @@ -181,7 +181,7 @@ Aside from the size, an area light has the same properties as a directional light. - The image below shows an example on how to light a component with different + The image shows an example on how to light a component with different colors using two different area lights. You can rotate, scale, and move area lights. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc index 8d001071cb..5f2f53ca96 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc @@ -32,7 +32,7 @@ By default, a directional light and a perspective camera are used in a 3D scene created by using the wizard template mentioned above. To use other light and camera types, select the component in the \uicontrol{3D} or - \uicontrol Navigatorn view and change the type of the component in the \uicontrol + \uicontrol Navigator view and change the type of the component in the \uicontrol Type field in \l Properties. For example, to use a point light, enter \e {PointLight}. diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json new file mode 100644 index 0000000000..e4201291c6 --- /dev/null +++ b/share/qtcreator/qmldesigner/designericons.json @@ -0,0 +1,81 @@ +{ + "ContextMenuArea": { + "size": "28x28", + "Off": { + "Disabled": { "color": "DSiconColorDisabled" }, + "Hovered": { "color": "DSnavigatorIconHover" }, + "Normal": { "color": "DSnavigatorIcon" }, + "Selected": { "color": "DSnavigatorIconSelected" } + }, + "On": { + "Disabled": { "color": "DSiconColorDisabled" }, + "Hovered": { "color": "DSnavigatorIconHover" }, + "Normal": { "color": "DSnavigatorIcon" }, + "Selected": { "color": "DSnavigatorIconSelected" } + } + }, + "AddMouseAreaIcon": { + "iconName": "s_mouseArea" + }, + "AnchorsIcon": { + "iconName": "s_anchors" + }, + "AnnotationIcon": { + "iconName": "s_annotations" + }, + "ArrangeIcon": { + "iconName": "s_arrange" + }, + "ConnectionsIcon": { + "iconName": "s_connections" + }, + "EditIcon": { + "iconName": "s_edit" + }, + "EnterComponentIcon": { + "iconName": "s_enterComponent" + }, + "EventListIcon": { + "iconName": "s_eventList" + }, + "GroupSelectionIcon": { + "iconName": "s_group" + }, + "LayoutsIcon": { + "iconName": "s_layouts" + }, + "MakeComponentIcon": { + "iconName": "s_component" + }, + "MergeWithTemplateIcon": { + "iconName": "s_merging" + }, + "PositionsersIcon": { + "iconName": "s_positioners" + }, + "SelecionIcon": { + "iconName": "s_selection" + }, + "ShowBoundsIcon": { + "Off": { + "iconName": "visibilityOff" + }, + "On": { + "iconName": "visibilityOn" + } + }, + "SnappingIcon": { + "iconName": "s_snapping" + }, + "TimelineIcon": { + "iconName": "s_timeline" + }, + "VisibilityIcon": { + "Off": { + "iconName": "visibilityOff" + }, + "On": { + "iconName": "visibilityOn" + } + } +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml index c717852649..1d5d2434c6 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml @@ -182,9 +182,36 @@ TreeViewDelegate { } ToolTip { + id: assetTooltip visible: !root.isFont && mouseArea.containsMouse && !root.assetsView.contextMenu.visible - text: model.filePath + text: assetTooltip.__computeText() delay: 1000 + + function __computeText() + { + let filePath = model.filePath.replace(assetsModel.contentDirPath(), "") + let fileSize = rootView.assetFileSize(model.filePath) + let fileExtMatches = model.filePath.match(/\.(.*)$/) + let fileExt = fileExtMatches ? "(" + fileExtMatches[1] + ")" : "" + + if (rootView.assetIsImage(model.filePath)) { + let size = rootView.imageSize(model.filePath) + + return filePath + "\n" + + size.width + " x " + size.height + + "\n" + fileSize + + " " + fileExt + } else { + return filePath + "\n" + + fileSize + + " " + fileExt + } + } + + function refresh() + { + text = assetTooltip.__computeText() + } } Timer { @@ -293,5 +320,10 @@ TreeViewDelegate { : "image://qmldesigner_assets/" + model.filePath } + onStatusChanged: { + if (thumbnailImage.status === Image.Ready) + assetTooltip.refresh() + } + } // Image } // TreeViewDelegate diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GeometrySection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GeometrySection.qml index 5e5d283a81..edf56c9b52 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GeometrySection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GeometrySection.qml @@ -47,6 +47,7 @@ Section { SectionLayout { PropertyLabel { text: qsTr("Position") + tooltip: qsTr("Sets the position of the component relative to its parent.") enabled: xSpinBox.enabled || ySpinBox.enabled } @@ -66,7 +67,7 @@ Section { ControlLabel { text: "X" - tooltip: xSpinBox.enabled ? "X" : root.disabledTooltip + tooltip: xSpinBox.enabled ? qsTr("X-coordinate") : root.disabledTooltip enabled: xSpinBox.enabled } @@ -87,7 +88,7 @@ Section { ControlLabel { text: "Y" - tooltip: xSpinBox.enabled ? "Y" : root.disabledTooltip + tooltip: xSpinBox.enabled ? qsTr("Y-coordinate") : root.disabledTooltip enabled: ySpinBox.enabled } /* @@ -101,6 +102,7 @@ Section { PropertyLabel { text: qsTr("Size") + tooltip: qsTr("Sets the width and height of the component.") enabled: widthSpinBox.enabled || heightSpinBox.enabled } @@ -157,11 +159,13 @@ Section { PropertyLabel { text: qsTr("Rotation") + tooltip: qsTr("Rotate the component at an angle.") blockedByTemplate: !backendValues.rotation.isAvailable } SecondColumnLayout { SpinBox { + id: rotationSpinBox implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: backendValues.rotation @@ -175,6 +179,7 @@ Section { ControlLabel { text: "°" + tooltip: rotationSpinBox.enabled ? qsTr("Angle (in degree)") : root.disabledTooltip enabled: backendValues.rotation.isAvailable } /* @@ -210,11 +215,13 @@ Section { PropertyLabel { text: qsTr("Scale") + tooltip: qsTr("Sets the scale of the component by percentage.") blockedByTemplate: !backendValues.scale.isAvailable } SecondColumnLayout { SpinBox { + id: scaleSpinBox implicitWidth: StudioTheme.Values.singleControlColumnWidth + StudioTheme.Values.actionIndicatorWidth sliderIndicatorVisible: true @@ -230,13 +237,17 @@ Section { ControlLabel { text: "%" + tooltip: scaleSpinBox.enabled ? qsTr("Percentage") : root.disabledTooltip enabled: backendValues.scale.isAvailable } ExpandingSpacer {} } - PropertyLabel { text: qsTr("Z stack") } + PropertyLabel { + text: qsTr("Z stack") + tooltip: qsTr("Sets the stacking order of the component.") + } SecondColumnLayout { SpinBox { @@ -252,6 +263,7 @@ Section { PropertyLabel { text: qsTr("Origin") + tooltip: qsTr("Sets the modification point of the component.") blockedByTemplate: !backendValues.transformOrigin.isAvailable } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml index 42805f6a79..e340ea9283 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml @@ -235,10 +235,10 @@ Section { ExpandingSpacer {} } -/* + PropertyLabel { - text: qsTr("Source Rectangle") - tooltip: qsTr("TODO.") + text: qsTr("Source rectangle") + tooltip: qsTr("Sets the rectangular area of the component that should be rendered into the texture.") } SecondColumnLayout { @@ -313,6 +313,5 @@ Section { ExpandingSpacer {} } -*/ } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 09858baef3..d8520d5cad 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -38,12 +38,21 @@ SecondColumnLayout { property alias spacer: spacer + property bool __block: false + function resetShapeColor() { colorEditor.backendValue.resetValue() } function initEditor() { + colorEditor.syncColor() + } + + // Syncing color from backend to frontend and block reflection + function syncColor() { + colorEditor.__block = true colorEditor.color = colorEditor.value + colorEditor.__block = false } Connections { @@ -52,12 +61,12 @@ SecondColumnLayout { function onValueChanged() { if (popupLoader.isNotInGradientMode()) - colorEditor.color = colorEditor.value + colorEditor.syncColor() } function onBackendValueChanged() { if (popupLoader.isNotInGradientMode()) - colorEditor.color = colorEditor.value + colorEditor.syncColor() } } @@ -83,6 +92,9 @@ SecondColumnLayout { } onColorChanged: { + if (colorEditor.__block) + return + if (!popupLoader.isInValidState) return @@ -211,24 +223,20 @@ SecondColumnLayout { if (popupLoader.active && popupLoader.dialog) popupLoader.dialog.determineActiveColorMode() else - colorEditor.color = colorEditor.value + colorEditor.syncColor() } property alias active: popupLoader.loader.active property Loader loader: Loader { - parent: colorEditor + parent: preview active: colorEditor.supportGradient - anchors.left: parent.left - anchors.leftMargin: 0 sourceComponent: ColorEditorPopup { id: cePopup x: cePopup.__defaultX y: cePopup.__defaultY } - onLoaded: { - popupLoader.dialog.initEditor() - } + onLoaded: popupLoader.dialog.initEditor() } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml index 5a9d5f5fe3..839c656d8a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml @@ -19,7 +19,10 @@ Section { property bool showState: false SectionLayout { - PropertyLabel { text: qsTr("Type") } + PropertyLabel { + text: qsTr("Type") + tooltip: qsTr("Sets the QML type of the component.") + } SecondColumnLayout { z: 2 @@ -44,7 +47,7 @@ Section { typeLineEdit.visible = !typeLineEdit.visible typeLineEdit.forceActiveFocus() } - tooltip: qsTr("Changes the type of this component.") + tooltip: qsTr("Sets the QML type of the component.") enabled: !modelNodeBackend.multiSelection } @@ -83,7 +86,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("ID") } + PropertyLabel { + text: qsTr("ID") + tooltip: qsTr("Sets a unique identification or name.") + } SecondColumnLayout { Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } @@ -167,7 +173,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Name") } + PropertyLabel { + text: qsTr("Name") + tooltip: qsTr("Adds a note with a title to explain the component.") + } SecondColumnLayout { enabled: !modelNodeBackend.multiSelection @@ -247,6 +256,7 @@ Section { PropertyLabel { visible: root.showState text: qsTr("State") + tooltip: qsTr("Sets the state of the component.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml index b5e001d64a..446bfc1c26 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml @@ -51,33 +51,6 @@ TextInput { border.width: 0 } - DragHandler { - id: dragHandler - target: null - acceptedDevices: PointerDevice.Mouse - enabled: true - - property int initialValue: 0 - - onActiveChanged: { - if (active) { - initialValue = myControl.value - mouseArea.cursorShape = Qt.ClosedHandCursor - myControl.drag = true - } else { - mouseArea.cursorShape = Qt.PointingHandCursor - myControl.drag = false - } - } - onTranslationChanged: { - var currValue = myControl.value - myControl.value = initialValue + translation.x - - if (currValue !== myControl.value) - myControl.valueModified() - } - } - Item { id: dragModifierWorkaround Keys.onPressed: function(event) { @@ -86,17 +59,20 @@ TextInput { if (event.modifiers & Qt.ControlModifier) { mouseArea.stepSize = myControl.minStepSize mouseArea.calcValue() + myControl.valueModified() } if (event.modifiers & Qt.ShiftModifier) { mouseArea.stepSize = myControl.maxStepSize mouseArea.calcValue() + myControl.valueModified() } } Keys.onReleased: function(event) { event.accepted = true - mouseArea.stepSize = myControl.realStepSize + mouseArea.stepSize = myControl.stepSize mouseArea.calcValue() + myControl.valueModified() } } @@ -117,13 +93,13 @@ TextInput { property int initialValue: myControl.value // value on drag operation starts - property int pressStartX: 0 - property int dragStartX: 0 - property int translationX: 0 + property real pressStartX: 0.0 + property real dragStartX: 0.0 + property real translationX: 0.0 - property int dragDirection: 0 - property int totalUnits: 0 // total number of units dragged - property int units: 0 + property real dragDirection: 0.0 + property real totalUnits: 0.0 // total number of units dragged + property real units: 0.0 property real __pixelsPerUnit: textInput.devicePixelRatio * textInput.pixelsPerUnit @@ -176,7 +152,7 @@ TextInput { mouseArea.translationX += translationX mouseArea.calcValue() - //myControl.realValueModified() + myControl.valueModified() } onClicked: function(mouse) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 412e1328ee..3df7a9e5dd 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -95,82 +95,109 @@ QtObject { 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 string imported: "\u0073" + readonly property string infinity: "\u0074" + readonly property string keyframe: "\u0075" + readonly property string linkTriangle: "\u0076" + readonly property string linked: "\u0077" + readonly property string listView: "\u0078" + readonly property string lockOff: "\u0079" + readonly property string lockOn: "\u007A" + readonly property string materialPreviewEnvironment: "\u007B" + readonly property string materialPreviewModel: "\u007C" + readonly property string mergeCells: "\u007D" + readonly property string minus: "\u007E" + readonly property string mirror: "\u007F" + readonly property string newMaterial: "\u0080" + readonly property string openLink: "\u0081" + readonly property string openMaterialBrowser: "\u0082" + readonly property string orientation: "\u0083" + readonly property string paddingEdge: "\u0084" + readonly property string paddingFrame: "\u0085" + readonly property string pasteStyle: "\u0086" + readonly property string pause: "\u0087" + readonly property string pin: "\u0088" + readonly property string play: "\u0089" + readonly property string plus: "\u008A" + readonly property string promote: "\u008B" + readonly property string readOnly: "\u008C" + readonly property string redo: "\u008D" + readonly property string rotationFill: "\u008E" + readonly property string rotationOutline: "\u008F" + readonly property string s_anchors: "\u0090" + readonly property string s_annotations: "\u0091" + readonly property string s_arrange: "\u0092" + readonly property string s_boundingBox: "\u0093" + readonly property string s_component: "\u0094" + readonly property string s_connections: "\u0095" + readonly property string s_edit: "\u0096" + readonly property string s_enterComponent: "\u0097" + readonly property string s_eventList: "\u0098" + readonly property string s_group: "\u0099" + readonly property string s_layouts: "\u009A" + readonly property string s_merging: "\u009B" + readonly property string s_mouseArea: "\u009D" + readonly property string s_positioners: "\u009E" + readonly property string s_selection: "\u009F" + readonly property string s_snapping: "\u00A0" + readonly property string s_timeline: "\u00A1" + readonly property string s_visibility: "\u00A2" + readonly property string search: "\u00A3" + readonly property string sectionToggle: "\u00A4" + readonly property string splitColumns: "\u00A5" + readonly property string splitRows: "\u00A6" + readonly property string startNode: "\u00A7" + readonly property string testIcon: "\u00A8" + readonly property string textAlignBottom: "\u00A9" + readonly property string textAlignCenter: "\u00AA" + readonly property string textAlignJustified: "\u00AB" + readonly property string textAlignLeft: "\u00AC" + readonly property string textAlignMiddle: "\u00AE" + readonly property string textAlignRight: "\u00AF" + readonly property string textAlignTop: "\u00B0" + readonly property string textBulletList: "\u00B1" + readonly property string textFullJustification: "\u00B2" + readonly property string textNumberedList: "\u00B3" + readonly property string tickIcon: "\u00B4" + readonly property string topToolbar_annotations: "\u00B5" + readonly property string topToolbar_closeFile: "\u00B6" + readonly property string topToolbar_designMode: "\u00B7" + readonly property string topToolbar_enterComponent: "\u00B8" + readonly property string topToolbar_home: "\u00B9" + readonly property string topToolbar_makeComponent: "\u00BA" + readonly property string topToolbar_navFile: "\u00BB" + readonly property string topToolbar_runProject: "\u00BC" + readonly property string translationCreateFiles: "\u00BD" + readonly property string translationCreateReport: "\u00BE" + readonly property string translationExport: "\u00BF" + readonly property string translationImport: "\u00C0" + readonly property string translationSelectLanguages: "\u00C1" + readonly property string translationTest: "\u00C2" + readonly property string transparent: "\u00C3" + readonly property string triState: "\u00C4" + readonly property string triangleArcA: "\u00C5" + readonly property string triangleArcB: "\u00C6" + readonly property string triangleCornerA: "\u00C7" + readonly property string triangleCornerB: "\u00C8" + readonly property string unLinked: "\u00C9" + readonly property string undo: "\u00CA" + readonly property string unpin: "\u00CB" + readonly property string upDownIcon: "\u00CC" + readonly property string upDownSquare2: "\u00CD" + readonly property string visibilityOff: "\u00CE" + readonly property string visibilityOn: "\u00CF" + readonly property string wildcard: "\u00D0" + readonly property string wizardsAutomotive: "\u00D1" + readonly property string wizardsDesktop: "\u00D2" + readonly property string wizardsGeneric: "\u00D3" + readonly property string wizardsMcuEmpty: "\u00D4" + readonly property string wizardsMcuGraph: "\u00D5" + readonly property string wizardsMobile: "\u00D6" + readonly property string wizardsUnknown: "\u00D7" + readonly property string zoomAll: "\u00D8" + readonly property string zoomIn: "\u00D9" + readonly property string zoomOut: "\u00DA" + readonly property string zoomSelection: "\u00DB" 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 Binary files differindex acd8df6ce3..cacd0c3cb9 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 3916a55929..40bc1dfa06 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -613,6 +613,7 @@ extend_qtc_plugin(QmlDesigner crumblebar.cpp crumblebar.h designeractionmanager.cpp designeractionmanager.h designeractionmanagerview.cpp designeractionmanagerview.h + designericons.cpp designericons.h findimplementation.cpp findimplementation.h layoutingridlayout.cpp layoutingridlayout.h modelnodecontextmenu.cpp modelnodecontextmenu.h diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index 3667a9f330..6fc9bd4a26 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -22,20 +22,13 @@ QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size, QPixmap pixmap; if (m_thumbnails.contains(id)) { - pixmap = m_thumbnails[id]; + pixmap = m_thumbnails[id].pixmap; } else { - pixmap = fetchPixmap(id, requestedSize); - bool haveValidImage = true; - if (pixmap.isNull()) { - pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png"); - haveValidImage = false; - } - - if (requestedSize.isValid()) - pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio); - - if (haveValidImage) - m_thumbnails[id] = pixmap; + Thumbnail thumbnail = createThumbnail(id, requestedSize); + pixmap = thumbnail.pixmap; + + if (thumbnail.assetType != Asset::MissingImage) + m_thumbnails[id] = thumbnail; } if (size) { @@ -46,6 +39,25 @@ QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size, return pixmap; } +Thumbnail AssetsLibraryIconProvider::createThumbnail(const QString &id, const QSize &requestedSize) +{ + auto [pixmap, fileSize] = fetchPixmap(id, requestedSize); + QSize originalSize = pixmap.size(); + Asset::Type assetType = Asset(id).type(); + + if (pixmap.isNull()) { + pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png"); + + if (assetType == Asset::Image) + assetType = Asset::MissingImage; + } + + if (requestedSize.isValid()) + pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio); + + return Thumbnail{pixmap, originalSize, assetType, fileSize}; +} + QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, const QSize &requestedSize) const { QSize reqSize = requestedSize.isValid() ? requestedSize : QSize{48, 48}; @@ -55,18 +67,25 @@ QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, co "Abc"}).pixmap(reqSize); } -QPixmap AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize) const +QPair<QPixmap, qint64> AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize) const { Asset asset(id); if (id == "browse") { - return Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/browse.png"); + QString filePath = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/browse.png"); + return {QPixmap{filePath}, 0}; } else if (asset.isFont()) { - return generateFontIcons(id, requestedSize); + qint64 size = QFileInfo(id).size(); + QPixmap pixmap = generateFontIcons(id, requestedSize); + return {pixmap, size}; } else if (asset.isImage()) { - return Utils::StyleHelper::dpiSpecificImageFile(id); - } else if (asset.isTexture3D()) { - return HdrImage{id}.toPixmap(); + QString filePath = Utils::StyleHelper::dpiSpecificImageFile(id); + qint64 size = QFileInfo(filePath).size(); + return {QPixmap{filePath}, size}; + } else if (asset.isHdrFile()) { + qint64 size = QFileInfo(id).size(); + QPixmap pixmap = HdrImage{id}.toPixmap(); + return {pixmap, size}; } else { QString type; if (asset.isShader()) @@ -81,9 +100,11 @@ QPixmap AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &r QString pathTemplate = QString(":/AssetsLibrary/images/asset_%1%2.png").arg(type); QString path = pathTemplate.arg('_' + QString::number(requestedSize.width())); - return Utils::StyleHelper::dpiSpecificImageFile(QFileInfo::exists(path) - ? path - : pathTemplate.arg("")); + QString filePath = Utils::StyleHelper::dpiSpecificImageFile(QFileInfo::exists(path) + ? path + : pathTemplate.arg("")); + qint64 size = QFileInfo(filePath).size(); + return {QPixmap{filePath}, size}; } } @@ -97,5 +118,23 @@ void AssetsLibraryIconProvider::invalidateThumbnail(const QString &id) m_thumbnails.remove(id); } +QSize AssetsLibraryIconProvider::imageSize(const QString &id) +{ + static QSize invalidSize = {}; + return m_thumbnails.contains(id) ? m_thumbnails[id].originalSize : invalidSize; +} + +qint64 AssetsLibraryIconProvider::fileSize(const QString &id) +{ + return m_thumbnails.contains(id) ? m_thumbnails[id].fileSize : 0; +} + +bool AssetsLibraryIconProvider::assetIsImage(const QString &id) +{ + return m_thumbnails.contains(id) + ? (m_thumbnails[id].assetType == Asset::Type::Image || Asset(id).isHdrFile()) + : false; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h index 61caba537a..b1a611d641 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h @@ -7,8 +7,18 @@ #include <QQuickImageProvider> +#include "asset.h" + namespace QmlDesigner { +struct Thumbnail +{ + QPixmap pixmap; + QSize originalSize; + Asset::Type assetType; + qint64 fileSize; +}; + class AssetsLibraryIconProvider : public QQuickImageProvider { public: @@ -17,10 +27,14 @@ public: QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; void clearCache(); void invalidateThumbnail(const QString &id); + QSize imageSize(const QString &id); + qint64 fileSize(const QString &id); + bool assetIsImage(const QString &id); private: QPixmap generateFontIcons(const QString &filePath, const QSize &requestedSize) const; - QPixmap fetchPixmap(const QString &id, const QSize &requestedSize) const; + QPair<QPixmap, qint64> fetchPixmap(const QString &id, const QSize &requestedSize) const; + Thumbnail createThumbnail(const QString &id, const QSize &requestedSize); SynchronousImageCache &m_fontImageCache; @@ -29,7 +43,7 @@ private: std::vector<QSize> iconSizes = {{128, 128}, // Drag {96, 96}, // list @2x {48, 48}}; // list - QHash<QString, QPixmap> m_thumbnails; + QHash<QString, Thumbnail> m_thumbnails; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 2a49012359..8a4a25c18d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -91,6 +91,11 @@ QString AssetsLibraryModel::currentProjectDirPath() const return DocumentManager::currentProjectDirPath().toString().append('/'); } +QString AssetsLibraryModel::contentDirPath() const +{ + return DocumentManager::currentResourcePath().toString().append('/'); +} + bool AssetsLibraryModel::requestDeleteFiles(const QStringList &filePaths) { bool askBeforeDelete = QmlDesignerPlugin::settings() diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 9239a97668..ff9832ee3c 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -40,6 +40,7 @@ public: Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const; Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const; Q_INVOKABLE QString currentProjectDirPath() const; + Q_INVOKABLE QString contentDirPath() const; Q_INVOKABLE bool requestDeleteFiles(const QStringList &filePaths); Q_INVOKABLE void deleteFiles(const QStringList &filePaths, bool dontAskAgain); Q_INVOKABLE bool renameFolder(const QString &folderPath, const QString &newName); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index d19ede82d5..d2c208929c 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -165,6 +165,22 @@ void AssetsLibraryWidget::invalidateThumbnail(const QString &id) m_assetsIconProvider->invalidateThumbnail(id); } +QSize AssetsLibraryWidget::imageSize(const QString &id) +{ + return m_assetsIconProvider->imageSize(id); +} + +QString AssetsLibraryWidget::assetFileSize(const QString &id) +{ + qint64 fileSize = m_assetsIconProvider->fileSize(id); + return QLocale::system().formattedDataSize(fileSize, 2, QLocale::DataSizeTraditionalFormat); +} + +bool AssetsLibraryWidget::assetIsImage(const QString &id) +{ + return m_assetsIconProvider->assetIsImage(id); +} + QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets() { return {}; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 60e9b165ee..980cae69dc 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -74,6 +74,10 @@ public: Q_INVOKABLE void openEffectMaker(const QString &filePath); Q_INVOKABLE bool qtVersionIsAtLeast6_4() const; Q_INVOKABLE void invalidateThumbnail(const QString &id); + Q_INVOKABLE QSize imageSize(const QString &id); + Q_INVOKABLE QString assetFileSize(const QString &id); + Q_INVOKABLE bool assetIsImage(const QString &id); + Q_INVOKABLE void addTextures(const QStringList &filePaths); Q_INVOKABLE void addLightProbe(const QString &filePaths); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 378a51d970..535703ac5f 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -6,6 +6,7 @@ #include "anchoraction.h" #include "changestyleaction.h" #include "designeractionmanagerview.h" +#include "designericons.h" #include "designermcumanager.h" #include "formatoperation.h" #include "modelnodecontextmenu_helper.h" @@ -286,14 +287,19 @@ QHash<QString, QStringList> DesignerActionManager::handleExternalAssetsDrop(cons return addedCategoryFiles; } +QIcon DesignerActionManager::contextIcon(int contextId) const +{ + return m_designerIcons->icon(DesignerIcons::IconId(contextId), DesignerIcons::ContextMenuArea); +} + class VisiblityModelNodeAction : public ModelNodeContextMenuAction { public: - VisiblityModelNodeAction(const QByteArray &id, const QString &description, const QByteArray &category, const QKeySequence &key, int priority, + VisiblityModelNodeAction(const QByteArray &id, const QString &description, const QIcon &icon, const QByteArray &category, const QKeySequence &key, int priority, SelectionContextOperation action, SelectionContextPredicate enabled = &SelectionContextFunctors::always, SelectionContextPredicate visibility = &SelectionContextFunctors::always) : - ModelNodeContextMenuAction(id, description, {}, category, key, priority, action, enabled, visibility) + ModelNodeContextMenuAction(id, description, icon, category, key, priority, action, enabled, visibility) {} void updateContext() override @@ -376,9 +382,9 @@ public: class SelectionModelNodeAction : public ActionGroup { public: - SelectionModelNodeAction(const QString &displayName, const QByteArray &menuId, int priority) : - ActionGroup(displayName, menuId, priority, - &SelectionContextFunctors::always, &SelectionContextFunctors::selectionEnabled) + SelectionModelNodeAction(const QString &displayName, const QByteArray &menuId, const QIcon &icon, int priority) : + ActionGroup(displayName, menuId, icon, priority, + &SelectionContextFunctors::always, &SelectionContextFunctors::selectionEnabled) {} @@ -599,9 +605,10 @@ void removeSignal(SignalHandlerProperty signalHandler) class ConnectionsModelNodeActionGroup : public ActionGroup { public: - ConnectionsModelNodeActionGroup(const QString &displayName, const QByteArray &menuId, int priority) + ConnectionsModelNodeActionGroup(const QString &displayName, const QByteArray &menuId, const QIcon &icon, int priority) : ActionGroup(displayName, menuId, + icon, priority, &SelectionContextFunctors::always, &SelectionContextFunctors::selectionEnabled) @@ -968,8 +975,8 @@ bool isFlowTargetOrTransition(const SelectionContext &context) class FlowActionConnectAction : public ActionGroup { public: - FlowActionConnectAction(const QString &displayName, const QByteArray &menuId, int priority) : - ActionGroup(displayName, menuId, priority, + FlowActionConnectAction(const QString &displayName, const QByteArray &menuId, const QIcon &icon, int priority) : + ActionGroup(displayName, menuId, icon, priority, &isFlowActionItemItem, &flowOptionVisible) {} @@ -1341,16 +1348,19 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new SelectionModelNodeAction( selectionCategoryDisplayName, selectionCategory, + contextIcon(DesignerIcons::SelecionIcon), Priorities::SelectionCategory)); addDesignerAction(new ConnectionsModelNodeActionGroup( connectionsCategoryDisplayName, connectionsCategory, + contextIcon(DesignerIcons::ConnectionsIcon), Priorities::ConnectionsCategory)); addDesignerAction(new ActionGroup( arrangeCategoryDisplayName, arrangeCategory, + contextIcon(DesignerIcons::ArrangeIcon), Priorities::ArrangeCategory, &selectionNotEmpty)); @@ -1408,7 +1418,11 @@ void DesignerActionManager::createDefaultDesignerActions() &reverse, &multiSelectionAndHasSameParent)); - addDesignerAction(new ActionGroup(editCategoryDisplayName, editCategory, Priorities::EditCategory, &selectionNotEmpty)); + addDesignerAction(new ActionGroup(editCategoryDisplayName, + editCategory, + contextIcon(DesignerIcons::EditIcon), + Priorities::EditCategory, + &selectionNotEmpty)); addDesignerAction(new SeperatorDesignerAction(editCategory, 30)); @@ -1491,6 +1505,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new VisiblityModelNodeAction( visiblityCommandId, visibilityDisplayName, + contextIcon(DesignerIcons::VisibilityIcon), rootCategory, QKeySequence("Ctrl+g"), Priorities::Visibility, @@ -1499,6 +1514,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ActionGroup(anchorsCategoryDisplayName, anchorsCategory, + contextIcon(DesignerIcons::AnchorsIcon), Priorities::AnchorsCategory, &anchorsMenuEnabled)); @@ -1596,18 +1612,21 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ActionGroup( positionerCategoryDisplayName, positionerCategory, + contextIcon(DesignerIcons::PositionsersIcon), Priorities::PositionCategory, &positionOptionVisible)); addDesignerAction(new ActionGroup( layoutCategoryDisplayName, layoutCategory, + contextIcon(DesignerIcons::LayoutsIcon), Priorities::LayoutCategory, &layoutOptionVisible)); addDesignerAction(new ActionGroup( snappingCategoryDisplayName, snappingCategory, + contextIcon(DesignerIcons::SnappingIcon), Priorities::SnappingCategory, &selectionEnabled, &selectionEnabled)); @@ -1615,12 +1634,14 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ActionGroup( groupCategoryDisplayName, groupCategory, + contextIcon(DesignerIcons::GroupSelectionIcon), Priorities::Group, &studioComponentsAvailableAndSelectionCanBeLayouted)); addDesignerAction(new ActionGroup( flowCategoryDisplayName, flowCategory, + {}, Priorities::FlowCategory, &isFlowTargetOrTransition, &flowOptionVisible)); @@ -1629,6 +1650,7 @@ void DesignerActionManager::createDefaultDesignerActions() auto effectMenu = new ActionGroup( flowEffectCategoryDisplayName, flowEffectCategory, + {}, Priorities::FlowCategory, &isFlowTransitionItem, &flowOptionVisible); @@ -1660,9 +1682,10 @@ void DesignerActionManager::createDefaultDesignerActions() &flowOptionVisible)); addDesignerAction(new FlowActionConnectAction( - flowConnectionCategoryDisplayName, - flowConnectionCategory, - Priorities::FlowCategory)); + flowConnectionCategoryDisplayName, + flowConnectionCategory, + {}, + Priorities::FlowCategory)); const QList<TypeName> transitionTypes = {"FlowFadeEffect", @@ -1688,6 +1711,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ActionGroup( stackedContainerCategoryDisplayName, stackedContainerCategory, + {}, Priorities::StackedContainerCategory, &isStackedContainer)); @@ -1888,7 +1912,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ModelNodeContextMenuAction( goIntoComponentCommandId, enterComponentDisplayName, - {}, + contextIcon(DesignerIcons::EnterComponentIcon), rootCategory, QKeySequence(Qt::Key_F2), Priorities::ComponentActions + 2, @@ -1898,7 +1922,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ModelNodeContextMenuAction( editAnnotationsCommandId, editAnnotationsDisplayName, - {}, + contextIcon(DesignerIcons::AnnotationIcon), rootCategory, QKeySequence(), Priorities::EditAnnotations, @@ -1907,15 +1931,15 @@ void DesignerActionManager::createDefaultDesignerActions() &singleSelection)); addDesignerAction(new ModelNodeContextMenuAction( - addMouseAreaFillCommandId, - addMouseAreaFillDisplayName, - {}, - rootCategory, - QKeySequence(), - Priorities::AddMouseArea, - &addMouseAreaFill, - &addMouseAreaFillCheck, - &singleSelection)); + addMouseAreaFillCommandId, + addMouseAreaFillDisplayName, + contextIcon(DesignerIcons::AddMouseAreaIcon), + rootCategory, + QKeySequence(), + Priorities::AddMouseArea, + &addMouseAreaFill, + &addMouseAreaFillCheck, + &singleSelection)); const bool standaloneMode = QmlProjectManager::QmlProject::isQtDesignStudio(); @@ -1943,7 +1967,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ModelNodeContextMenuAction( makeComponentCommandId, makeComponentDisplayName, - {}, + contextIcon(DesignerIcons::MakeComponentIcon), rootCategory, QKeySequence(), Priorities::ComponentActions + 1, @@ -1954,7 +1978,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ModelNodeContextMenuAction( editMaterialCommandId, editMaterialDisplayName, - {}, + contextIcon(DesignerIcons::EditIcon), rootCategory, QKeySequence(), 44, @@ -1974,6 +1998,7 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ActionGroup( "", genericToolBarCategory, + {}, Priorities::GenericToolBar)); addDesignerAction(new ChangeStyleAction()); @@ -2114,6 +2139,7 @@ DesignerActionManager::DesignerActionManager(DesignerActionManagerView *designer : m_designerActionManagerView(designerActionManagerView) , m_externalDependencies(externalDependencies) { + setupIcons(); } DesignerActionManager::~DesignerActionManager() = default; @@ -2145,6 +2171,20 @@ void DesignerActionManager::addCustomTransitionEffectAction() &isFlowTransitionItem)); } +void DesignerActionManager::setupIcons() +{ + m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath())); +} + +QString DesignerActionManager::designerIconResourcesPath() const +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/designericons.json"; +#endif + return Core::ICore::resourcePath("qmldesigner/designericons.json").toString(); +} + DesignerActionToolBar::DesignerActionToolBar(QWidget *parentWidget) : Utils::StyledBar(parentWidget), m_toolBar(new QToolBar("ActionToolBar", this)) { diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 10b9eb6ae1..569c495f88 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -23,6 +23,7 @@ QT_END_NAMESPACE namespace QmlDesigner { class DesignerActionManagerView; +class DesignerIcons; using AddResourceOperation = std::function<AddFilesResult(const QStringList &, const QString &, bool)>; using ModelNodePreviewImageOperation = std::function<QVariant(const ModelNode &)>; @@ -118,16 +119,20 @@ public: ModelNodePreviewImageOperation modelNodePreviewOperation(const ModelNode &node) const; bool externalDragHasSupportedAssets(const QMimeData *data) const; QHash<QString, QStringList> handleExternalAssetsDrop(const QMimeData *data) const; + QIcon contextIcon(int contextId) const; private: void addTransitionEffectAction(const TypeName &typeName); void addCustomTransitionEffectAction(); + void setupIcons(); + QString designerIconResourcesPath() const; QList<QSharedPointer<ActionInterface> > m_designerActions; DesignerActionManagerView *m_designerActionManagerView; QList<AddResourceHandler> m_addResourceHandler; QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers; ExternalDependenciesInterface &m_externalDependencies; + QScopedPointer<DesignerIcons> m_designerIcons; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.cpp b/src/plugins/qmldesigner/components/componentcore/designericons.cpp new file mode 100644 index 0000000000..ce604d6d8c --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/designericons.cpp @@ -0,0 +1,418 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "designericons.h" + +#include <invalidargumentexception.h> + +#include <QCache> +#include <QFile> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QMetaEnum> +#include <QRegularExpression> + +using namespace QmlDesigner; +namespace { // Blank namespace + +template <typename EType> +struct DesignerIconEnums +{ + typedef EType EnumType; + static QString toString(const EnumType &enumValue); + static EnumType value(const QString &keyStr, bool *ok = nullptr); + + static const QMetaEnum metaEnum; + static const QString keyName; +}; + +template <typename EType> +struct DesignerEnumConfidentType +{ + typedef EType EnumType; +}; + +template <> +struct DesignerEnumConfidentType<QIcon::Mode> +{ + typedef DesignerIcons::Mode EnumType; +}; + +template <> +struct DesignerEnumConfidentType<QIcon::State> +{ + typedef DesignerIcons::State EnumType; +}; + +template <typename T> +QString getEnumName() { + QMetaEnum metaEnum = QMetaEnum::fromType<T>(); + QString enumName = QString::fromLatin1(metaEnum.enumName()); + if (enumName.size() && enumName.at(0).isUpper()) + enumName.replace(0, 1, enumName.at(0).toLower()); + return enumName; +}; + +template <> +QString getEnumName<Theme::Icon>() { + return QLatin1String("iconName"); +}; + +template <typename EType> +const QMetaEnum DesignerIconEnums<EType>::metaEnum = + QMetaEnum::fromType<typename DesignerEnumConfidentType<EType>::EnumType>(); + +template <typename EType> +const QString DesignerIconEnums<EType>::keyName = + getEnumName<typename DesignerEnumConfidentType<EType>::EnumType>(); + +template <typename EType> +QString DesignerIconEnums<EType>::toString(const EType &enumValue) +{ + return QString::fromLatin1(metaEnum.valueToKey(enumValue)); +} + +template <typename EType> +EType DesignerIconEnums<EType>::value(const QString &keyStr, bool *ok) +{ + return static_cast<EType>(metaEnum.keyToValue(keyStr.toLatin1(), ok)); +} + +Q_GLOBAL_STATIC(QStringList, _iconFontMandatoryKeys); + +const QStringList & iconFontMandatoryKeys() +{ + if (_iconFontMandatoryKeys->isEmpty()) { + *_iconFontMandatoryKeys + << DesignerIconEnums<Theme::Icon>::keyName + << DesignerIconEnums<Theme::Color>::keyName + << DesignerIconEnums<QIcon::Mode>::keyName + << DesignerIconEnums<QIcon::State>::keyName + << "size"; + } + return *_iconFontMandatoryKeys; +} + +QJsonObject mergeJsons(const QJsonObject &prior, const QJsonObject &joiner) +{ + QJsonObject object = prior; + const QStringList joinerKeys = joiner.keys(); + for (const QString &joinerKey : joinerKeys) { + if (!object.contains(joinerKey)) { + object.insert(joinerKey, joiner.value(joinerKey)); + } else { + QJsonValue ov = object.value(joinerKey); + QJsonValue jv = joiner.value(joinerKey); + if (ov.isObject() && jv.isObject()) { + QJsonObject mg = mergeJsons(ov.toObject(), jv.toObject()); + object.insert(joinerKey, mg); + } + } + } + return object; +} + +inline QString toJsonSize(const QSize &size) +{ + return QString::fromLatin1("%1x%2").arg(size.width()).arg(size.height()); +} + +template <typename EnumType> +void pushSimpleEnumValue(QJsonObject &object, const EnumType &enumVal) +{ + const QString &enumKey = DesignerIconEnums<EnumType>::keyName; + QString enumValue = DesignerIconEnums<EnumType>::toString(enumVal); + object.insert(enumKey, enumValue); +} + +template <typename T> +T jsonSafeValue(const QJsonObject &jsonObject, const QString &symbolName, + std::function<bool (const T&)> validityCheck = [](const T&) -> bool {return true;}) +{ + if (!jsonObject.contains(symbolName)) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, symbolName.toLatin1()); + + QVariant symbolVar = jsonObject.value(symbolName); + T extractedVal = symbolVar.value<T>(); + if (!validityCheck(extractedVal)) + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, symbolName.toLatin1()); + + return extractedVal; +} + +QSize jsonSafeSize(const QJsonObject &jsonObject, const QString &symbolName) +{ + QString extractedVal = jsonSafeValue<QString>(jsonObject, symbolName); + QStringList dims = extractedVal.split("x"); + if (dims.size() == 2) { + bool wOk; + bool hOk; + int cWidth = dims.first().toInt(&wOk); + int cHeight = dims.last().toInt(&hOk); + if (wOk && hOk) + return {cWidth, cHeight}; + } + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, symbolName.toLatin1()); + return {}; +} + +template <typename T> +T jsonSafeMetaEnum(const QJsonObject &jsonObject, const QString &symbolName = DesignerIconEnums<T>::keyName) +{ + QString extractedVal = jsonSafeValue<QString>(jsonObject, symbolName); + bool ok; + T enumIndex = static_cast<T> (DesignerIconEnums<T>::value(extractedVal, &ok)); + if (ok) + return enumIndex; + + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, symbolName.toLatin1()); + return {}; +} + +template <typename T> +struct JsonMap +{}; + +template <> +struct JsonMap<IconFontHelper> +{ + static IconFontHelper value(const QJsonObject &jsonObject, const QJsonObject &telescopingMap) + { + QJsonObject fontHelperJson = mergeJsons(jsonObject, telescopingMap); + return IconFontHelper::fromJson(fontHelperJson); + } + + static QJsonObject json(const IconFontHelper &iconFontHelper) + { + QJsonObject object; + pushSimpleEnumValue(object, iconFontHelper.themeIcon()); + pushSimpleEnumValue(object, iconFontHelper.themeColor()); + object.insert("size", toJsonSize(iconFontHelper.size())); + return object; + } +}; + +template <typename Key, typename Value> +struct JsonMap<QMap<Key, Value>> +{ + typedef QMap<Key, Value> MapType; + static MapType value(const QJsonObject &mapObject, const QJsonObject &telescopingMap) + { + typedef typename MapType::key_type KeyType; + typedef typename MapType::mapped_type ValueType; + + QMap<KeyType, ValueType> output; + QJsonObject curObject = mergeJsons(mapObject, telescopingMap); + const QStringList keyList = curObject.keys(); + QStringList validKeys; + QString keyTypeStr = DesignerIconEnums<KeyType>::keyName; + QJsonObject nextTelescopingMap = telescopingMap; + + for (const QString &jsonKey : keyList) { + bool keyAvailable = false; + DesignerIconEnums<KeyType>::value(jsonKey, &keyAvailable); + if (keyAvailable) + validKeys.append(jsonKey); + else + nextTelescopingMap.insert(jsonKey, curObject.value(jsonKey)); + } + + for (const QString &jsonKey : validKeys) { + bool keyAvailable = false; + const KeyType key = DesignerIconEnums<KeyType>::value(jsonKey, &keyAvailable); + QJsonValue jsonValue = curObject.value(jsonKey); + + nextTelescopingMap.insert(keyTypeStr, jsonKey); + if (!jsonValue.isObject()) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Value of the" << jsonKey << "should be a json object."; + continue; + } + output.insert(key, JsonMap<ValueType>::value(jsonValue.toObject(), nextTelescopingMap)); + } + + return output; + } + + static QJsonObject json(const QMap<Key, Value> &map) + { + QJsonObject output; + for (const auto &[key, val] : map.asKeyValueRange()) + output[DesignerIconEnums<Key>::toString(key)] = JsonMap<Value>::json(val); + + return output; + } +}; + +} // End of blank namespace + +class QmlDesigner::DesignerIconsPrivate +{ +public: + DesignerIconsPrivate(const QString &fontName) + : mFontName(fontName) {} + + QString mFontName; + DesignerIcons::IconsMap icons; + static QCache<QString, DesignerIcons::IconsMap> cache; +}; + +QCache<QString, DesignerIcons::IconsMap> DesignerIconsPrivate::cache(100); + +IconFontHelper::IconFontHelper(Theme::Icon themeIcon, Theme::Color color, const QSize &size, QIcon::Mode mode, QIcon::State state) + : Super(Theme::getIconUnicode(themeIcon), + Theme::getColor(color), + size, + mode, state) + , mThemeIcon(themeIcon) + , mThemeColor(color) +{} + +IconFontHelper IconFontHelper::fromJson(const QJsonObject &jsonObject) +{ + try { + Theme::Icon iconName = jsonSafeMetaEnum<Theme::Icon>(jsonObject); + Theme::Color iconColor = jsonSafeMetaEnum<Theme::Color>(jsonObject); + QSize iconSize = jsonSafeSize(jsonObject, "size"); + QIcon::Mode iconMode = jsonSafeMetaEnum<QIcon::Mode>(jsonObject); + QIcon::State iconState = jsonSafeMetaEnum<QIcon::State>(jsonObject); + return IconFontHelper(iconName, iconColor, iconSize, iconMode, iconState); + } catch (const Exception &exception) { + exception.showException("Faild to load IconFontHelper"); + return {}; + } +} + +QJsonObject IconFontHelper::toJson() const +{ + QJsonObject jsonObject; + pushSimpleEnumValue(jsonObject, themeIcon()); + pushSimpleEnumValue(jsonObject, themeColor()); + pushSimpleEnumValue(jsonObject, mode()); + pushSimpleEnumValue(jsonObject, state()); + jsonObject.insert("size", toJsonSize(size())); + return jsonObject; +} + +Theme::Icon IconFontHelper::themeIcon() const +{ + return mThemeIcon; +} + +Theme::Color IconFontHelper::themeColor() const +{ + return mThemeColor; +} + +IconFontHelper::IconFontHelper() + : IconFontHelper({}, {}, {}, {}, {}) {} + +DesignerIcons::DesignerIcons(const QString &fontName, const QString &iconDatabase) + : d(new DesignerIconsPrivate(fontName)) +{ + if (iconDatabase.size()) + loadIconSettings(iconDatabase); +} + +DesignerIcons::~DesignerIcons() +{ + delete d; +} + +QIcon DesignerIcons::icon(IconId icon, Area area) const +{ + return Utils::StyleHelper::getIconFromIconFont(d->mFontName, helperList(icon, area)); +} + +void DesignerIcons::loadIconSettings(const QString &fileName) +{ + if (d->cache.contains(fileName)) { + d->icons = *d->cache.object(fileName); + return; + } + + QFile designerIconFile(fileName); + + if (!designerIconFile.open(QFile::ReadOnly)) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Can not open file:" << fileName << designerIconFile.errorString(); + return; + } + + if (designerIconFile.size() > 100e3) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Large File:" << fileName; + return; + } + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(designerIconFile.readAll(), &parseError); + + if (parseError.error != QJsonParseError::NoError) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Json Parse Error - " << parseError.errorString() << "---\t File: " << fileName; + return; + } + + if (!jsonDoc.isObject()) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Invalid Json Array in file: " << fileName; + return; + } + + clearAll(); + d->icons = JsonMap<IconsMap>::value(jsonDoc.object(), {}); + d->cache.insert(fileName, new IconsMap(d->icons), 1); +} + +void DesignerIcons::exportSettings(const QString &fileName) const +{ + QFile outFile(fileName); + if (!outFile.open(QFile::WriteOnly)) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Can not open file for writing:" << fileName; + return; + } + + QJsonDocument jsonDocument; + jsonDocument.setObject(JsonMap<IconsMap>::json(d->icons)); + + outFile.write(jsonDocument.toJson()); + outFile.close(); +} + +void DesignerIcons::clearAll() +{ + d->icons.clear(); +} + +void DesignerIcons::addIcon(const IconId &iconId, const Area &area, const IconFontHelper &fontHelper) +{ + AreaMap &areaMap = d->icons[iconId]; + IconMap &iconMap = areaMap[area]; + ModeMap &modeMap = iconMap[static_cast<State>(fontHelper.state())]; + modeMap.insert(static_cast<Mode>(fontHelper.mode()), fontHelper); +} + +void DesignerIcons::addIcon(IconId iconId, + Area area, + QIcon::Mode mode, + QIcon::State state, + Theme::Icon themeIcon, + Theme::Color color, + const QSize &size) +{ + addIcon(iconId, area, {themeIcon, color, size, mode, state}); +} + +void DesignerIcons::addIcon(IconId iconId, Area area, Theme::Icon themeIcon, Theme::Color color, const QSize &size) +{ + addIcon(iconId, area, {themeIcon, color, size}); +} + +QList<Utils::StyleHelper::IconFontHelper> DesignerIcons::helperList(const IconId &iconId, + const Area &area) const +{ + QList<Utils::StyleHelper::IconFontHelper> helperList; + const IconMap &iconMap = d->icons.value(iconId).value(area); + for (const ModeMap &modMap : iconMap) { + for (const IconFontHelper &iconFontHelper : modMap) + helperList.append(iconFontHelper); + } + return helperList; +} diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h new file mode 100644 index 0000000000..342016b77c --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -0,0 +1,133 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "theme.h" + +#include <utils/stylehelper.h> + +#include <QIcon> + +namespace QmlDesigner { + +class DesignerIconsPrivate; + +class IconFontHelper : public Utils::StyleHelper::IconFontHelper +{ + typedef Utils::StyleHelper::IconFontHelper Super; + +public: + IconFontHelper(Theme::Icon themeIcon, + Theme::Color color, + const QSize &size, + QIcon::Mode mode = QIcon::Normal, + QIcon::State state = QIcon::Off); + + static IconFontHelper fromJson(const QJsonObject &jsonObject); + QJsonObject toJson() const; + Theme::Icon themeIcon() const; + Theme::Color themeColor() const; + +private: + IconFontHelper(); + + Theme::Icon mThemeIcon; + Theme::Color mThemeColor; +}; + +class DesignerIcons +{ + Q_GADGET + friend DesignerIconsPrivate; + +public: + enum IconId { + AddMouseAreaIcon, + AlignBottomIcon, + AlignLeftIcon, + AlignRightIcon, + AlignTopIcon, + AnchorsIcon, + AnnotationIcon, + ArrangeIcon, + ConnectionsIcon, + EditIcon, + EnterComponentIcon, + EventListIcon, + GroupSelectionIcon, + LayoutsIcon, + MakeComponentIcon, + MergeWithTemplateIcon, + PositionsersIcon, + SelecionIcon, + SnappingIcon, + TimelineIcon, + ShowBoundsIcon, + VisibilityIcon + }; + Q_ENUM(IconId) + + enum Area { + TopToolbarArea, + ContextMenuArea + }; + Q_ENUM(Area) + + enum Mode { + Normal = QIcon::Normal, + Disabled = QIcon::Disabled, + Hovered = QIcon::Active, + Selected = QIcon::Selected + }; + + Q_ENUM(Mode) + + enum State { + Off = QIcon::Off, + On = QIcon::On + }; + Q_ENUM(State) + + typedef QMap<Mode, IconFontHelper> ModeMap; + typedef QMap<State, ModeMap> IconMap; + typedef QMap<Area, IconMap> AreaMap; + typedef QMap<IconId, AreaMap> IconsMap; + + explicit DesignerIcons(const QString &fontName, + const QString &iconDatabase = QString()); + + ~DesignerIcons(); + + QIcon icon(IconId icon, Area area) const; + + void loadIconSettings(const QString &fileName); + void exportSettings(const QString &fileName) const; + + void clearAll(); + void addIcon(const IconId &iconId, + const Area &area, + const IconFontHelper &fontHelper); + + void addIcon(IconId iconId, + Area area, + QIcon::Mode mode, + QIcon::State state, + Theme::Icon themeIcon, + Theme::Color color, + const QSize &size); + + void addIcon(IconId iconId, + Area area, + Theme::Icon themeIcon, + Theme::Color color, + const QSize &size); + +private: + QList<Utils::StyleHelper::IconFontHelper> helperList(const IconId &iconId, + const Area &area) const; + + DesignerIconsPrivate *d = nullptr; +}; + +} diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp index 31a996b5be..eac545b2ef 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp @@ -60,7 +60,7 @@ void populateMenu(QSet<ActionInterface* > &actionInterfaces, || actionInterface->type() == ActionInterface::FormEditorAction) { QAction* action = actionInterface->action(); actionInterface->currentContextChanged(selectionContext); - action->setIconVisibleInMenu(false); + action->setIconVisibleInMenu(true); menu->addAction(action); } } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index ed1de892f9..50f5e83bd8 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -141,7 +141,7 @@ public: class ActionGroup : public AbstractActionGroup { public: - ActionGroup(const QString &displayName, const QByteArray &menuId, int priority, + ActionGroup(const QString &displayName, const QByteArray &menuId, const QIcon &icon, int priority, SelectionContextPredicate enabled = &SelectionContextFunctors::always, SelectionContextPredicate visibility = &SelectionContextFunctors::always) : AbstractActionGroup(displayName), @@ -150,6 +150,7 @@ public: m_enabled(enabled), m_visibility(visibility) { + menu()->setIcon(icon); } bool isVisible(const SelectionContext &m_selectionState) const override { return m_visibility(m_selectionState); } diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 85163b985a..f6bc8cc0bf 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -103,6 +103,7 @@ public: gridView, idAliasOff, idAliasOn, + imported, infinity, keyframe, linkTriangle, @@ -131,6 +132,24 @@ public: redo, rotationFill, rotationOutline, + s_anchors, + s_annotations, + s_arrange, + s_boundingBox, + s_component, + s_connections, + s_edit, + s_enterComponent, + s_eventList, + s_group, + s_layouts, + s_merging, + s_mouseArea, + s_positioners, + s_selection, + s_snapping, + s_timeline, + s_visibility, search, sectionToggle, splitColumns, @@ -148,6 +167,14 @@ public: textFullJustification, textNumberedList, tickIcon, + topToolbar_annotations, + topToolbar_closeFile, + topToolbar_designMode, + topToolbar_enterComponent, + topToolbar_home, + topToolbar_makeComponent, + topToolbar_navFile, + topToolbar_runProject, translationCreateFiles, translationCreateReport, translationExport, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 20bb6b3419..cb03b5906c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -5,9 +5,6 @@ #include "edit3dview.h" #include "edit3dwidget.h" -#include "nodehints.h" -#include "qmlvisualnode.h" - #include <bindingproperty.h> #include <nodemetainfo.h> #include <nodelistproperty.h> @@ -143,41 +140,6 @@ void Edit3DCanvas::resizeEvent(QResizeEvent *e) m_parent->view()->edit3DViewResized(e->size()); } -void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e) -{ - // Block all drags if scene root node is locked - ModelNode node; - if (m_parent->view()->hasModelNodeForInternalId(m_activeScene)) - node = m_parent->view()->modelNodeForInternalId(m_activeScene); - - // Allow drop when there is no valid active scene, as the drop goes under the root node of - // the document in that case. - if (!ModelNode::isThisOrAncestorLocked(node)) { - QByteArray data = e->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); - if (!data.isEmpty()) { - QDataStream stream(data); - stream >> m_itemLibraryEntry; - if (NodeHints::fromItemLibraryEntry(m_itemLibraryEntry).canBeDroppedInView3D()) - e->accept(); - } - } -} - -void Edit3DCanvas::dropEvent(QDropEvent *e) -{ - m_parent->view()->executeInTransaction(__FUNCTION__, [&] { - auto modelNode = QmlVisualNode::createQml3DNode(m_parent->view(), m_itemLibraryEntry, m_activeScene).modelNode(); - QTC_ASSERT(modelNode.isValid(), return); - - e->accept(); - m_parent->view()->setSelectedModelNode(modelNode); - - // if added node is a Model, assign it a material - if (modelNode.metaInfo().isQtQuick3DModel()) - m_parent->view()->assignMaterialTo3dModel(modelNode); - }); -} - void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent) { QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_3DEDITOR_TIME, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index a755fbff98..d575e68c13 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "itemlibraryinfo.h" - #include <QtWidgets/qwidget.h> #include <QtGui/qimage.h> #include <QtGui/qevent.h> @@ -38,8 +36,6 @@ protected: void keyReleaseEvent(QKeyEvent *e) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; - void dragEnterEvent(QDragEnterEvent *e) override; - void dropEvent(QDropEvent *e) override; void focusOutEvent(QFocusEvent *focusEvent) override; void focusInEvent(QFocusEvent *focusEvent) override; @@ -49,7 +45,6 @@ private: QPointer<Edit3DWidget> m_parent; QImage m_image; qint32 m_activeScene = -1; - ItemLibraryEntry m_itemLibraryEntry; QElapsedTimer m_usageTimer; qreal m_opacity = 1.0; QWidget *m_busyIndicator = nullptr; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 7a3f7cd3cd..afd4558f5c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -16,6 +16,7 @@ #include "qmldesignerconstants.h" #include "qmldesignericons.h" #include "qmldesignerplugin.h" +#include "qmlvisualnode.h" #include "seekerslider.h" #include <coreplugin/icore.h> @@ -304,6 +305,16 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos if (modelNode.isValid() && !modelNode.isSelected()) setSelectedModelNode(modelNode); m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode, pos3d); + } else if (m_nodeAtPosReqType == NodeAtPosReqType::ComponentDrop) { + ModelNode createdNode; + executeInTransaction(__FUNCTION__, [&] { + createdNode = QmlVisualNode::createQml3DNode( + this, m_droppedEntry, edit3DWidget()->canvas()->activeScene(), pos3d).modelNode(); + if (createdNode.metaInfo().isQtQuick3DModel()) + assignMaterialTo3dModel(createdNode); + }); + if (createdNode.isValid()) + setSelectedModelNode(createdNode); } else if (m_nodeAtPosReqType == NodeAtPosReqType::MaterialDrop) { bool isModel = modelNode.metaInfo().isQtQuick3DModel(); if (m_droppedModelNode.isValid() && modelNode.isValid() && isModel) { @@ -895,4 +906,11 @@ void Edit3DView::dropTexture(const ModelNode &textureNode, const QPointF &pos) emitView3DAction(View3DActionType::GetNodeAtPos, pos); } +void Edit3DView::dropComponent(const ItemLibraryEntry &entry, const QPointF &pos) +{ + m_nodeAtPosReqType = NodeAtPosReqType::ComponentDrop; + m_droppedEntry = entry; + emitView3DAction(View3DActionType::GetNodeAtPos, pos); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 236fd10aa6..03dc13b4ac 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once +#include "itemlibraryinfo.h" #include <qmldesignercomponents_global.h> #include <abstractview.h> @@ -65,6 +66,7 @@ public: void dropMaterial(const ModelNode &matNode, const QPointF &pos); void dropBundleMaterial(const QPointF &pos); void dropTexture(const ModelNode &textureNode, const QPointF &pos); + void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos); private slots: void onEntriesChanged(); @@ -72,6 +74,7 @@ private slots: private: enum class NodeAtPosReqType { BundleMaterialDrop, + ComponentDrop, MaterialDrop, TextureDrop, ContextMenu, @@ -122,6 +125,7 @@ private: int particlemode; ModelCache<QImage> m_canvasCache; ModelNode m_droppedModelNode; + ItemLibraryEntry m_droppedEntry; NodeAtPosReqType m_nodeAtPosReqType; QPoint m_contextMenuPos; QTimer m_compressionTimer; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 40b524614b..701237dd71 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dwidget.h" -#include "designdocumentview.h" +#include "designdocument.h" #include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dview.h" @@ -10,10 +10,10 @@ #include "metainfo.h" #include "modelnodeoperations.h" #include "nodeabstractproperty.h" +#include "nodehints.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "qmlvisualnode.h" -#include "timelineactions.h" #include "viewmanager.h" #include <auxiliarydataproperties.h> @@ -249,6 +249,16 @@ bool Edit3DWidget::isPasteAvailable() const return QApplication::clipboard()->text().startsWith(Constants::HEADER_3DPASTE_CONTENT); } +bool Edit3DWidget::isSceneLocked() const +{ + if (m_view && m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { + ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); + if (ModelNode::isThisOrAncestorLocked(node)) + return true; + } + return false; +} + // Called by the view to update the "create" sub-menu when the Quick3D entries are ready. void Edit3DWidget::updateCreateSubMenu(const QStringList &keys, const QHash<QString, QList<ItemLibraryEntry>> &entriesMap) @@ -287,7 +297,7 @@ void Edit3DWidget::updateCreateSubMenu(const QStringList &keys, void Edit3DWidget::onCreateAction() { QAction *action = qobject_cast<QAction *>(sender()); - if (!action || !m_view || !m_view->model()) + if (!action || !m_view || !m_view->model() || isSceneLocked()) return; m_view->executeInTransaction(__FUNCTION__, [&] { @@ -374,6 +384,9 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode const bool anyNodeSelected = view()->hasSelectedModelNodes(); const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected(); + if (m_createSubMenu) + m_createSubMenu->setEnabled(!isSceneLocked()); + m_editComponentAction->setEnabled(isSingleComponent); m_editMaterialAction->setEnabled(isModel); m_duplicateAction->setEnabled(selectionExcludingRoot); @@ -407,6 +420,15 @@ Edit3DView *Edit3DWidget::view() const void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) { + // Block all drags if scene root node is locked + if (m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { + ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); + if (ModelNode::isThisOrAncestorLocked(node)) + return; + } + + m_draggedEntry = {}; + const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() ->viewManager().designerActionManager(); if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()) @@ -414,6 +436,14 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { dragEnterEvent->acceptProposedAction(); + } else if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { + QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); + if (!data.isEmpty()) { + QDataStream stream(data); + stream >> m_draggedEntry; + if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D()) + dragEnterEvent->acceptProposedAction(); + } } } @@ -443,6 +473,13 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) return; } + // handle dropping from component view + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { + if (!m_draggedEntry.name().isEmpty()) + m_view->dropComponent(m_draggedEntry, pos); + return; + } + // handle dropping external assets const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() ->viewManager().designerActionManager(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 8a1304a016..728d81646d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -52,6 +52,7 @@ private: void createContextMenu(); bool isPasteAvailable() const; + bool isSceneLocked() const; QPointer<Edit3DView> m_edit3DView; QPointer<Edit3DView> m_view; @@ -77,6 +78,7 @@ private: ModelNode m_contextMenuTarget; QVector3D m_contextMenuPos3d; QHash<QString, ItemLibraryEntry> m_nameToEntry; + ItemLibraryEntry m_draggedEntry; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp index fb9bba3fd1..9d57112747 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp @@ -3,6 +3,7 @@ #include "eventlistpluginview.h" #include "assigneventdialog.h" #include "connectsignaldialog.h" +#include "designericons.h" #include "eventlistactions.h" #include "eventlistdialog.h" @@ -45,6 +46,7 @@ void EventListPluginView::registerActions() designerActionManager.addDesignerAction(new ActionGroup(tr("Event List"), ComponentCoreConstants::eventListCategory, + designerActionManager.contextIcon(DesignerIcons::EventListIcon), ComponentCoreConstants::Priorities::EventListCategory, &SelectionContextFunctors::always, &SelectionContextFunctors::always)); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index 36a9970f58..648a51b91b 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -3,6 +3,7 @@ #include "formeditorwidget.h" #include "designeractionmanager.h" +#include "designericons.h" #include "designersettings.h" #include "formeditoritem.h" #include "formeditorscene.h" @@ -104,6 +105,7 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view) m_showBoundingRectAction = new QAction(tr("Show Bounds"), this); m_showBoundingRectAction->setCheckable(true); m_showBoundingRectAction->setChecked(false); + m_showBoundingRectAction->setIcon(DesignerActionManager::instance().contextIcon(DesignerIcons::ShowBoundsIcon)); registerActionAsCommand(m_showBoundingRectAction, Constants::FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE, QKeySequence(Qt::Key_A), diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index d17dbdbc71..a99d4133af 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -4,13 +4,16 @@ #include "materialeditorcontextobject.h" #include <abstractview.h> +#include <bindingproperty.h> +#include <documentmanager.h> #include <nodemetainfo.h> #include <rewritingexception.h> #include <qmldesignerplugin.h> #include <qmlmodelnodeproxy.h> #include <qmlobjectnode.h> #include <qmltimeline.h> -#include <documentmanager.h> +#include <qmltimelinekeyframegroup.h> +#include <variantproperty.h> #include <coreplugin/messagebox.h> #include <utils/algorithm.h> @@ -129,6 +132,48 @@ void MaterialEditorContextObject::changeTypeName(const QString &typeName) incompatibleProperties.append(property.name()); } + // When switching between material types, copy base (diffuse) color and map properties of + // source type into corresponding properties of the target type. + const QList<PropertyName> baseColors = {"baseColor", "diffuseColor", "albedoColor"}; + const QList<PropertyName> baseMaps = {"baseColorMap", "diffuseMap", "albedoMap"}; + int sourceIndex = -1; + int targetIndex = -1; + NodeMetaInfo oldMetaInfo = m_selectedMaterial.metaInfo(); + struct CopyData { + CopyData() {}; + CopyData(PropertyName n) : name(n) {} + PropertyName name; + QVariant value; + bool isBinding = false; + }; + QHash<PropertyName, CopyData> copyMap; + + if (oldMetaInfo.isQtQuick3DPrincipledMaterial()) + sourceIndex = 0; + else if (oldMetaInfo.isQtQuick3DDefaultMaterial()) + sourceIndex = 1; + else if (oldMetaInfo.isQtQuick3DSpecularGlossyMaterial()) + sourceIndex = 2; + + if (metaInfo.isQtQuick3DPrincipledMaterial()) + targetIndex = 0; + else if (metaInfo.isQtQuick3DDefaultMaterial()) + targetIndex = 1; + else if (metaInfo.isQtQuick3DSpecularGlossyMaterial()) + targetIndex = 2; + + if (sourceIndex >= 0 && targetIndex >= 0) { + if (incompatibleProperties.contains(baseColors[sourceIndex])) { + copyMap.insert(baseColors[sourceIndex], baseColors[targetIndex]); + incompatibleProperties.removeOne(baseColors[sourceIndex]); + } + if (incompatibleProperties.contains(baseMaps[sourceIndex])) { + copyMap.insert(baseMaps[sourceIndex], baseMaps[targetIndex]); + incompatibleProperties.removeOne(baseMaps[sourceIndex]); + } + } + const auto ©Keys = copyMap.keys(); + Utils::sort(incompatibleProperties); // Create a dialog showing incompatible properties and signals @@ -159,10 +204,47 @@ void MaterialEditorContextObject::changeTypeName(const QString &typeName) m_selectedMaterial.removeProperty(p); } + if (!copyKeys.isEmpty()) { + // Copy mapped properties to new name. Note that this will only copy the base + // property value and adjust the keyframe groups. Any other bindings related + // to the property will be ignored. + const QList<ModelNode> timeLines = QmlObjectNode(m_selectedMaterial).allTimelines(); + for (const auto &key : std::as_const(copyKeys)) { + CopyData ©Data = copyMap[key]; + for (const auto &timeLineNode : timeLines) { + QmlTimeline timeLine(timeLineNode); + if (timeLine.hasKeyframeGroup(m_selectedMaterial, key)) { + QmlTimelineKeyframeGroup group = timeLine.keyframeGroup(m_selectedMaterial, + key); + group.setPropertyName(copyData.name); + } + } + // Property value itself cannot be copied until type has been changed, so store it + AbstractProperty prop = m_selectedMaterial.property(key); + if (prop.isValid()) { + if (prop.isBindingProperty()) { + copyData.isBinding = true; + copyData.value = prop.toBindingProperty().expression(); + } else { + copyData.value = prop.toVariantProperty().value(); + } + } + m_selectedMaterial.removeProperty(key); + } + } + if (m_selectedMaterial.isRootNode()) rewriterView->changeRootNodeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); else m_selectedMaterial.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); + + for (const auto &key : copyKeys) { + const CopyData ©Data = copyMap[key]; + if (copyData.isBinding) + m_selectedMaterial.bindingProperty(copyData.name).setExpression(copyData.value.toString()); + else + m_selectedMaterial.variantProperty(copyData.name).setValue(copyData.value); + } }); } diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 91104b8c63..836e473f8a 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -39,6 +39,8 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i // -> Model // BundleMaterial // -> Model + // Effect + // -> Item if (insertInfo.isQtQuick3DTexture()) { if (parentInfo.isQtQuick3DDefaultMaterial() || parentInfo.isQtQuick3DPrincipledMaterial() @@ -88,11 +90,14 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i || parentInfo.isQtQuick3DParticles3DAttractor3D()) propertyList.append("shape"); } else if (insertInfo.isQtQuick3DMaterial()) { - if (parentInfo.isQtQuick3DParticles3DModel()) + if (parentInfo.isQtQuick3DModel()) + propertyList.append("materials"); + } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { + if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); -// TODO merge conflict between Change-Id: If3c58f82797beabe76baf99ea2dddc59032729df and Change-Id: Iff2dea66e253b412105427134bd49cb16ed76193 -// } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { -// if (parentInfo.isSubclassOf("QtQuick3D.Model")) + } else if (insertInfo.isEffectMaker()) { + if (parentInfo.isQtQuickItem()) + propertyList.append("effect"); } } diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index 58e83d2060..9f2f0854b3 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -211,21 +211,23 @@ void NameItemDelegate::paint(QPainter *painter, } ModelNode node = getModelNode(modelIndex); - NavigatorWidget *widget = qobject_cast<NavigatorWidget *>(styleOption.widget->parent()); - if (widget && !widget->dragType().isEmpty()) { - QByteArray dragType = widget->dragType(); - const NodeMetaInfo metaInfo = node.metaInfo(); - const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType); - ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true); - - if (!filter->propertyList.isEmpty()) { - painter->setOpacity(0.5); - painter->fillRect(styleOption.rect.adjusted(0, delegateMargin, 0, -delegateMargin), - Theme::getColor(Theme::Color::DSnavigatorDropIndicatorBackground)); - painter->setOpacity(1.0); - painter->setPen(Theme::getColor(Theme::Color::DSnavigatorTextSelected)); + if (!ModelNode::isThisOrAncestorLocked(node)) { + NavigatorWidget *widget = qobject_cast<NavigatorWidget *>(styleOption.widget->parent()); + if (widget && !widget->dragType().isEmpty()) { + QByteArray dragType = widget->dragType(); + const NodeMetaInfo metaInfo = node.metaInfo(); + const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType); + ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true); + + if (!filter->propertyList.isEmpty()) { + painter->setOpacity(0.5); + painter->fillRect(styleOption.rect.adjusted(0, delegateMargin, 0, -delegateMargin), + Theme::getColor(Theme::Color::DSnavigatorDropIndicatorBackground)); + painter->setOpacity(1.0); + painter->setPen(Theme::getColor(Theme::Color::DSnavigatorTextSelected)); + } + delete filter; } - delete filter; } iconOffset = drawIcon(painter, styleOption, modelIndex); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 0f8a21b68d..ccf9bafd9a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -1019,7 +1019,8 @@ ModelNode NavigatorTreeModel::handleItemLibraryEffectDrop(const QString &effectP ModelNode targetNode(modelNodeForIndex(rowModelIndex)); ModelNode newModelNode; - if (targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect") + if ((targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect") + || !targetNode.metaInfo().isQtQuickItem()) return newModelNode; if (ModelNodeOperations::validateEffect(effectPath)) { diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 9d080e9274..e6450d4562 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -7,6 +7,8 @@ #include "qmldesignerconstants.h" #include "qmldesignericons.h" #include "qmldesignerplugin.h" +#include "assetslibrarywidget.h" +#include "commontypecache.h" #include "nameitemdelegate.h" #include "iconcheckboxitemdelegate.h" @@ -266,6 +268,18 @@ void NavigatorView::dragStarted(QMimeData *mimeData) m_widget->setDragType(bundleMatType); m_widget->update(); + } else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) { + const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); + if (assetsPaths.count() > 0) { + auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetsPaths[0]); + QString assetType = assetTypeAndData.first; + if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { + // We use arbitrary type name because at this time we don't have effect maker + // specific type + m_widget->setDragType(Storage::Info::EffectMaker); + m_widget->update(); + } + } } } @@ -389,6 +403,13 @@ void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, [[maybe_unused]] const QVariant &data) { m_currentModelInterface->notifyDataChanged(modelNode); + + if (key == lockedProperty) { + // Also notify data changed on child nodes to redraw them + const QList<ModelNode> childNodes = modelNode.allSubModelNodes(); + for (const auto &childNode : childNodes) + m_currentModelInterface->notifyDataChanged(childNode); + } } void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp index b7bfd88e5b..6cb65a9562 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp @@ -36,7 +36,7 @@ QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QSt response->setImage(image.scaled(requestedSize, Qt::KeepAspectRatio)); return; } - } else if (asset.isTexture3D()) { + } else if (asset.isHdrFile()) { HdrImage hdr{asset.id()}; if (!hdr.image().isNull()) { response->setImage(hdr.image().scaled(requestedSize, Qt::KeepAspectRatio)); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index e35dd7dcd1..0ad9a8741b 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -3,6 +3,7 @@ #include "timelineview.h" +#include "designericons.h" #include "easingcurve.h" #include "timelineactions.h" #include "timelineconstants.h" @@ -584,6 +585,7 @@ void TimelineView::registerActions() actionManager.addDesignerAction(new ActionGroup(TimelineConstants::timelineCategoryDisplayName, TimelineConstants::timelineCategory, + actionManager.contextIcon(DesignerIcons::TimelineIcon), ComponentCoreConstants::Priorities::TimelineCategory, timelineEnabled, &SelectionContextFunctors::always)); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index ceb931b7d2..35f2c25f46 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -100,6 +100,7 @@ public: bool isAlias() const; bool isBool() const; bool isColor() const; + bool isEffectMaker() const; bool isFloat() const; bool isFlowViewFlowActionArea() const; bool isFlowViewFlowDecision() const; @@ -129,7 +130,6 @@ public: bool isQtQuick3DNode() const; bool isQtQuick3DParticles3DAffector3D() const; bool isQtQuick3DParticles3DAttractor3D() const; - bool isQtQuick3DParticles3DModel() const; bool isQtQuick3DParticles3DParticle3D() const; bool isQtQuick3DParticles3DParticleEmitter3D() const; bool isQtQuick3DParticles3DSpriteParticle3D() const; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index aa057533c8..7aaed70c9d 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -640,14 +640,12 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, case AuxiliaryDataType::Document: if ((key == lockedProperty || key == invisibleProperty) && hasInstanceForModelNode(node)) { NodeInstance instance = instanceForModelNode(node); - if (value.isValid()) { - PropertyValueContainer container{instance.instanceId(), - PropertyName{key.name}, - value, - TypeName(), - key.type}; - m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - } + PropertyValueContainer container{instance.instanceId(), + PropertyName{key.name}, + value, + TypeName(), + key.type}; + m_nodeInstanceServer->changeAuxiliaryValues({{container}}); }; break; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index ca78be4012..d15ef9227c 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -343,6 +343,8 @@ static inline bool isValueType(const TypeName &type) "QPointF", "QSize", "QSizeF", + "QRect", + "QRectF", "QVector2D", "QVector3D", "QVector4D", @@ -361,6 +363,8 @@ static inline bool isValueType(const QString &type) "QPointF", "QSize", "QSizeF", + "QRect", + "QRectF", "QVector2D", "QVector3D", "QVector4D", @@ -405,7 +409,7 @@ QVector<PropertyInfo> getQmlTypes(const CppComponentValue *objectValue, const Co if (objectValue->className().isEmpty()) return propertyList; - if (rec > 2) + if (rec > 4) return propertyList; PropertyMemberProcessor processor(context); @@ -427,9 +431,14 @@ QVector<PropertyInfo> getQmlTypes(const CppComponentValue *objectValue, const Co } } if (isValueType(objectValue->propertyType(nameAsString))) { - const ObjectValue *dotObjectValue = value_cast<ObjectValue>(objectValue->lookupMember(nameAsString, context)); + const ObjectValue *dotObjectValue = value_cast<ObjectValue>( + objectValue->lookupMember(nameAsString, context)); + if (dotObjectValue) { - const QVector<PropertyInfo> dotProperties = getObjectTypes(dotObjectValue, context, false, rec + 1); + const QVector<PropertyInfo> dotProperties = getObjectTypes(dotObjectValue, + context, + false, + rec + 1); for (const PropertyInfo &propertyInfo : dotProperties) { const PropertyName dotName = name + '.' + propertyInfo.first; const TypeName type = propertyInfo.second; @@ -521,7 +530,7 @@ QVector<PropertyInfo> getObjectTypes(const ObjectValue *objectValue, const Conte if (objectValue->className().isEmpty()) return propertyList; - if (rec > 2) + if (rec > 4) return propertyList; PropertyMemberProcessor processor(context); @@ -535,6 +544,7 @@ QVector<PropertyInfo> getObjectTypes(const ObjectValue *objectValue, const Conte if (isValueType(property.second)) { const Value *dotValue = objectValue->lookupMember(nameAsString, context); + if (!dotValue) continue; @@ -542,7 +552,10 @@ QVector<PropertyInfo> getObjectTypes(const ObjectValue *objectValue, const Conte dotValue = context->lookupReference(ref); if (const ObjectValue *dotObjectValue = dotValue->asObjectValue()) { - const QVector<PropertyInfo> dotProperties = getObjectTypes(dotObjectValue, context, false, rec + 1); + const QVector<PropertyInfo> dotProperties = getObjectTypes(dotObjectValue, + context, + false, + rec + 1); for (const PropertyInfo &propertyInfo : dotProperties) { const PropertyName dotName = name + '.' + propertyInfo.first; const TypeName type = propertyInfo.second; @@ -2535,6 +2548,13 @@ bool NodeMetaInfo::isColor() const } } +bool NodeMetaInfo::isEffectMaker() const +{ + // We use arbitrary type name because at this time we don't have effect maker + // specific type + return typeName() == QString::fromUtf8(Storage::Info::EffectMaker); +} + bool NodeMetaInfo::isBool() const { if constexpr (useProjectStorage()) { @@ -2724,17 +2744,6 @@ bool NodeMetaInfo::isQtQuick3DView3D() const } } -bool NodeMetaInfo::isQtQuick3DParticles3DModel() const -{ - if constexpr (useProjectStorage()) { - using namespace Storage::Info; - return isBasedOnCommonType<QtQuick3D_Particles3D, Storage::Info::Model>(m_projectStorage, - m_typeId); - } else { - return isValid() && isSubclassOf("QtQuick3D.Particles3D.Model"); - } -} - bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 9b112d759e..af612ca3f4 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1392,8 +1392,7 @@ ModelNode ModelNode::lowestCommonAncestor(const QList<ModelNode> &nodes) ModelNode accumulatedNode = nodes.first(); int accumulatedNodeDepth = -1; - Utils::span<const ModelNode> nodesExceptFirst(nodes.constBegin() + 1, nodes.constEnd()); - for (const ModelNode &node : nodesExceptFirst) { + for (const ModelNode &node : Utils::span<const ModelNode>(nodes).subspan(1)) { accumulatedNode = QmlDesigner::lowestCommonAncestor(accumulatedNode, node, accumulatedNodeDepth, diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 39d14fd545..ca321b5687 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -280,7 +280,8 @@ bool isListElementType(const QmlDesigner::TypeName &type) bool isComponentType(const QmlDesigner::TypeName &type) { return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" - || type == "QtQml.Component" || type == "<cpp>.QQmlComponent" || type == "QQmlComponent"; + || type == "QtQml.Component" || type == "<cpp>.QQmlComponent" || type == "QQmlComponent" + || type == "QML.Component"; } bool isCustomParserType(const QmlDesigner::TypeName &type) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 36f1b9bc98..4878b59783 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -35,6 +35,7 @@ inline constexpr char DefaultMaterial[] = "DefaultMaterial"; inline constexpr char Dialog[] = "Dialog"; inline constexpr char DoubleType[] = "double"; inline constexpr char Effect[] = "Effect"; +inline constexpr char EffectMaker[] = "EffectMaker"; inline constexpr char FloatType[] = "float"; inline constexpr char FlowActionArea[] = "FlowActionArea"; inline constexpr char FlowDecision[] = "FlowDecision"; diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp index ca5bff17e3..70d62f71c6 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp @@ -40,6 +40,7 @@ QmlPreviewWidgetPlugin::QmlPreviewWidgetPlugin() designerActionManager.addDesignerAction(new ActionGroup( QString(), ComponentCoreConstants::qmlPreviewCategory, + {}, ComponentCoreConstants::Priorities::QmlPreviewCategory, &SelectionContextFunctors::always)); s_previewPlugin = getPreviewPlugin(); diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index ec420e2720..87b9edcbc0 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -535,7 +535,7 @@ MetaInfo { } Type { - name: "QtQml.Component" + name: "QML.Component" icon: ":/qtquickplugin/images/component-icon16.png" Hints { @@ -547,7 +547,7 @@ MetaInfo { name: "Component" category: "e.Qt Quick - Component" libraryIcon: ":/qtquickplugin/images/component-icon.png" - version: "2.0" + version: "1.0" QmlSource { source: ":/qtquickplugin/source/component.qml" } toolTip: qsTr("Allows you to define components inline, within a QML document.") @@ -555,7 +555,7 @@ MetaInfo { } Type { - name: "QtQml.Component" + name: "QML.Component" icon: ":/qtquickplugin/images/component-icon16.png" Hints { @@ -567,7 +567,7 @@ MetaInfo { name: "Component 3D" category: "Qt Quick 3D Component" libraryIcon: ":/qtquickplugin/images/component-icon.png" - version: "2.0" + version: "1.0" requiredImport: "QtQuick3D" QmlSource { source: ":/qtquickplugin/source/component3d.qml" } diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp index 4b8eaaabf7..5ec84d5bbc 100644 --- a/src/plugins/qmldesigner/utils/asset.cpp +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -154,6 +154,11 @@ bool Asset::isTexture3D() const return type() == Asset::Type::Texture3D; } +bool Asset::isHdrFile() const +{ + return m_suffix == "*.hdr"; +} + bool Asset::isEffect() const { return type() == Asset::Type::Effect; diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h index 408131fa57..3e6d105ec4 100644 --- a/src/plugins/qmldesigner/utils/asset.h +++ b/src/plugins/qmldesigner/utils/asset.h @@ -8,7 +8,7 @@ namespace QmlDesigner { class Asset { public: - enum Type { Unknown, Image, FragmentShader, Font, Audio, Video, Texture3D, Effect, Shader }; + enum Type { Unknown, Image, MissingImage, FragmentShader, Font, Audio, Video, Texture3D, Effect, Shader }; Asset(const QString &filePath); @@ -34,6 +34,7 @@ public: bool isAudio() const; bool isVideo() const; bool isTexture3D() const; + bool isHdrFile() const; bool isEffect() const; bool isSupported() const; diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index 3ca420d60a..d9c9a25be3 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -223,8 +223,9 @@ static void showUpdateInfo(const QList<Update> &updates, auto label = new QLabel; label->setText("<qt><p>" + UpdateInfoPlugin::tr("Available updates:") + "<ul><li>" + qtText + updateText + "</li></ul></p></qt>"); - label->setContentsMargins(0, 0, 0, 8); + label->setContentsMargins(2, 2, 2, 2); auto scrollArea = new QScrollArea; + scrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); scrollArea->setWidget(label); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->viewport()->setAutoFillBackground(false); diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index b390b9d97f..281e6030a0 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -567,6 +567,15 @@ void ObjectNodeInstance::doResetProperty(const PropertyName &propertyName) QmlPrivateGate::doResetProperty(object(), context(), propertyName); } +static bool isPropertyBlackListed(const PropertyName &propertyName) +{ + if (propertyName.contains(".") && propertyName.contains("__")) + return true; + if (propertyName.count(".") > 2) + return true; + return false; +} + QVariant ObjectNodeInstance::property(const PropertyName &name) const { if (ignoredProperties().contains(name)) @@ -574,7 +583,7 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const // TODO: handle model nodes - if (QmlPrivateGate::isPropertyBlackListed(name)) + if (isPropertyBlackListed(name)) return QVariant(); QQmlProperty property(object(), QString::fromUtf8(name), context()); @@ -612,6 +621,37 @@ void ObjectNodeInstance::ensureVector3DDotProperties(PropertyNameList &list) con } } +void ObjectNodeInstance::ensureValueTypeProperties(PropertyNameList &list) const +{ + const PropertyNameList pointDotProperties = {"x", "y"}; + const PropertyNameList sizeDotProperties = {"width", "height"}; + const PropertyNameList rectDotProperties = {"x", "y", "width", "height"}; + + PropertyNameList valueTypeProperties; + + for (const auto &property : list) { + const QString name = instanceType(property); + PropertyNameList dotProperties; + + if (name == "QPoint" || name == "QPointF") + dotProperties = pointDotProperties; + + if (name == "QSize" || name == "QSizeF") + dotProperties = sizeDotProperties; + + if (name == "QRect" || name == "QRectF") + dotProperties = rectDotProperties; + + for (const auto &dotProperty : dotProperties) + valueTypeProperties.append(property + "." + dotProperty); + } + + for (const auto &valueTypeProperty : valueTypeProperties) { + if (!list.contains(valueTypeProperty)) + list.append(valueTypeProperty); + } +} + PropertyNameList ObjectNodeInstance::propertyNames() const { PropertyNameList list; @@ -619,13 +659,14 @@ PropertyNameList ObjectNodeInstance::propertyNames() const list = QmlPrivateGate::allPropertyNames(object()); ensureVector3DDotProperties(list); + ensureValueTypeProperties(list); return list; } QString ObjectNodeInstance::instanceType(const PropertyName &name) const { - if (QmlPrivateGate::isPropertyBlackListed(name)) + if (isPropertyBlackListed(name)) return QLatin1String("undefined"); QQmlProperty property(object(), QString::fromUtf8(name), context()); diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h index 85e663e3ee..36abf66494 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h @@ -199,6 +199,7 @@ protected: void initializePropertyWatcher(const ObjectNodeInstance::Pointer &objectNodeInstance); void ensureVector3DDotProperties(PropertyNameList &list) const; + void ensureValueTypeProperties(PropertyNameList &list) const; private: QString m_id; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index a96f4a4217..36c24d8573 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -345,7 +345,7 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks( if (helper) { QSet<QQuick3DNode *> blockedNodes; QSet<QQuick3DNode *> unblockedNodes; - const PropertyName rotBlocked = "rotBlocked"; + const PropertyName rotBlocked = "rotBlock"; for (const auto &container : valueChanges) { if (container.name() == rotBlocked && container.auxiliaryDataType() == AuxiliaryDataType::NodeInstanceAuxiliary) { diff --git a/src/tools/qml2puppet/qml2puppet/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h index a0f1fd345a..c73bd7fcc6 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlbase.h +++ b/src/tools/qml2puppet/qml2puppet/qmlbase.h @@ -40,18 +40,22 @@ public: , m_args({argc, argv}) { m_argParser.setApplicationDescription("QML Runtime Provider for QDS"); - m_argParser.addOptions( - {{"qml-puppet", "Run QML Puppet (default)"}, - {"qml-runtime", "Run QML Runtime"}, - {"appinfo", "Print build information"}, - {"test", "Run test mode"} - }); + m_argParser.addOptions({{"qml-puppet", "Run QML Puppet (default)"}, + {"qml-runtime", "Run QML Runtime"}, + {"appinfo", "Print build information"}, + {"test", "Run test mode"}}); } int run() { populateParser(); initCoreApp(); + + if (!m_coreApp) { //default to QGuiApplication + createCoreApp<QGuiApplication>(); + qWarning() << "CoreApp is not initialized! Falling back to QGuiApplication!"; + } + initParser(); initQmlRunner(); return m_coreApp->exec(); @@ -88,11 +92,6 @@ private: QCommandLineOption optHelp = m_argParser.addHelpOption(); QCommandLineOption optVers = m_argParser.addVersionOption(); - if (!m_coreApp) { - qCritical() << "Cannot initialize coreapp!"; - m_argParser.showHelp(); - } - if (!m_argParser.parse(m_coreApp->arguments())) { std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl << std::endl; diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp index d53531d44c..a4ade2b2b4 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp @@ -241,7 +241,8 @@ void QmlRuntime::initQmlRunner() loadConf(confFile, !m_verboseMode); // Load files - QScopedPointer<LoadWatcher> lw(new LoadWatcher(m_qmlEngine.data(), files.size(), m_conf.data())); + LoadWatcher *lw = new LoadWatcher(m_qmlEngine.data(), files.size(), m_conf.data()); + lw->setParent(this); for (const QString &path : std::as_const(files)) { QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile); diff --git a/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml index ca4618ba73..7b99d4530a 100644 --- a/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml +++ b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml @@ -4,22 +4,46 @@ import QtQuick.Window 2.0 import QtQuick 2.0 Window { + id: window property Item containedObject: null - property bool __resizeGuard: false + + readonly property Item firstChild: window.contentItem.children.length > 0 ? window.contentItem.children[0] : null + + property bool writeGuard: false + + onFirstChildChanged: { + window.writeGuard = true + window.containedObject = window.firstChild + window.writeGuard = false + } + onContainedObjectChanged: { + if (window.writeGuard) + return + if (containedObject == undefined || containedObject == null) { visible = false return } - __resizeGuard = true - width = containedObject.width - height = containedObject.height + + window.width = containedObject.width + window.height = containedObject.height + containedObject.parent = contentItem - visible = true - __resizeGuard = false + window.visible = true + } + + Binding { + target: window.firstChild + when: window.firstChild + property: "height" + value: window.height + } + + Binding { + target: window.firstChild + when: window.firstChild + property: "width" + value: window.width } - onWidthChanged: if (!__resizeGuard && containedObject) - containedObject.width = width - onHeightChanged: if (!__resizeGuard && containedObject) - containedObject.height = height } |