diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc index bbe9a910db6..d4127574a69 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt5.qrc @@ -16,6 +16,8 @@ mockfiles/images/static_floor.png mockfiles/images/spot.png mockfiles/images/spot@2x.png + mockfiles/images/preview_landscape.hdr + mockfiles/images/preview_studio.hdr mockfiles/qt5/AdjustableArrow.qml mockfiles/qt5/AreaLightHandle.qml mockfiles/qt5/Arrow.qml diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc index 05459423349..bb29d683a07 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc @@ -18,6 +18,8 @@ mockfiles/images/floor_tex.png mockfiles/images/spot.png mockfiles/images/spot@2x.png + mockfiles/images/preview_landscape.hdr + mockfiles/images/preview_studio.hdr mockfiles/qt6/AdjustableArrow.qml mockfiles/qt6/AreaLightHandle.qml mockfiles/qt6/Arrow.qml diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr new file mode 100644 index 00000000000..2c6b4372d47 Binary files /dev/null and b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_landscape.hdr differ diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr new file mode 100644 index 00000000000..8ab060c1f45 Binary files /dev/null and b/share/qtcreator/qml/qmlpuppet/mockfiles/images/preview_studio.hdr differ diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index 9fee06e0ad1..067e34928b7 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -29,8 +29,12 @@ View3D { id: root anchors.fill: parent environment: sceneEnv + camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera property Material previewMaterial + property string envMode + property string envValue + property string modelSrc: "#Sphere" function fitToViewPort(closeUp) { @@ -41,28 +45,59 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + backgroundMode: envMode === "Color" ? SceneEnvironment.Color + : envMode === "SkyBox" ? SceneEnvironment.SkyBox + : SceneEnvironment.Transparent + clearColor: envMode === "Color" ? envValue : "#000000" + lightProbe: envMode === "SkyBox" ? skyBoxTex : null + + Texture { + id: skyBoxTex + source: envMode === "SkyBox" ? "../images/" + envValue + ".hdr" + : "" + } } Node { DirectionalLight { eulerRotation.x: -26 - eulerRotation.y: -57 + eulerRotation.y: modelSrc === "#Cube" ? -10 : -50 + brightness: envMode !== "SkyBox" ? 100 : 0 } PerspectiveCamera { - y: 125.331 - z: 120 - eulerRotation.x: -31 + id: defaultCamera + y: 70 + z: 200 + eulerRotation.x: -5.71 clipNear: 1 clipFar: 1000 } - Model { - id: model + PerspectiveCamera { + id: studioCamera + y: 232 + z: 85 + eulerRotation.x: -64.98 + clipNear: 1 + clipFar: 1000 + } + Node { + rotation: root.camera.rotation y: 50 - source: "#Sphere" - materials: previewMaterial + Node { + y: modelSrc === "#Cone" ? -40 : 10 + eulerRotation.x: 35 + Model { + id: model + source: modelSrc ? modelSrc : "#Sphere" + eulerRotation.y: 45 + materials: previewMaterial + scale: !modelSrc || modelSrc === "#Sphere" + ? Qt.vector3d(1.7, 1.7, 1.7) : Qt.vector3d(1.2, 1.2, 1.2) + } + } } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index 70b9dbc4d0e..d32a1ea915d 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -50,25 +50,28 @@ Item { view.destroy(); } - function createViewForObject(obj) + function createViewForObject(obj, env, envValue, model) { if (obj instanceof Material) - createViewForMaterial(obj); + createViewForMaterial(obj, env, envValue, model); else if (obj instanceof Model) createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); } - function createViewForMaterial(material) + function createViewForMaterial(material, env, envValue, model) { if (!materialViewComponent) materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); // Always recreate the view to ensure material is up to date - if (materialViewComponent.status === Component.Ready) - view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); - + if (materialViewComponent.status === Component.Ready) { + view = materialViewComponent.createObject(viewRect, {"previewMaterial": material, + "envMode": env, + "envValue": envValue, + "modelSrc": model}); + } previewObject = material; } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index 94051d5f6e6..12fdd3048eb 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -29,8 +29,12 @@ View3D { id: root anchors.fill: parent environment: sceneEnv + camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera property Material previewMaterial + property string envMode + property string envValue + property string modelSrc: "#Sphere" function fitToViewPort(closeUp) { @@ -41,30 +45,79 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + backgroundMode: envMode === "Color" ? SceneEnvironment.Color + : envMode === "SkyBox" ? SceneEnvironment.SkyBox + : SceneEnvironment.Transparent + clearColor: envMode === "Color" ? envValue : "#000000" + lightProbe: envMode === "SkyBox" ? skyBoxTex : null + + Texture { + id: skyBoxTex + source: envMode === "SkyBox" ? "../images/" + envValue + ".hdr" + : "" + } } Node { DirectionalLight { eulerRotation.x: -26 - eulerRotation.y: -57 + eulerRotation.y: modelSrc === "#Cube" ? -10 : -50 + brightness: envMode !== "SkyBox" ? 1 : 0 } PerspectiveCamera { - y: 125.331 - z: 120 - eulerRotation.x: -31 + id: defaultCamera + y: 70 + z: 200 + eulerRotation.x: -5.71 clipNear: 1 clipFar: 1000 } - Model { - id: model - readonly property bool _edit3dLocked: true // Make this non-pickable - - y: 50 - source: "#Sphere" - materials: previewMaterial + PerspectiveCamera { + id: studioCamera + y: 232 + z: 85 + eulerRotation.x: -64.98 + clipNear: 1 + clipFar: 1000 } + Node { + rotation: root.camera.rotation + y: 50 + Node { + y: modelSrc === "#Cone" ? -40 : 10 + eulerRotation.x: 35 + Model { + id: model + readonly property bool _edit3dLocked: true // Make this non-pickable + source: modelSrc ? modelSrc : "#Sphere" + eulerRotation.y: 45 + materials: previewMaterial + scale: !modelSrc || modelSrc === "#Sphere" + ? Qt.vector3d(1.7, 1.7, 1.7) : Qt.vector3d(1.2, 1.2, 1.2) + } + } + } + Model { + id: floorModel + source: "#Rectangle" + scale.y: 8 + scale.x: 8 + eulerRotation.x: -60 + visible: !envMode || envMode === "Default" + materials: floorMaterial + DefaultMaterial { + id: floorMaterial + diffuseMap: floorTex + Texture { + id: floorTex + source: "../images/floor_tex.png" + scaleU: floorModel.scale.x + scaleV: floorModel.scale.y + } + } + } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 031d01d65fb..e409e35aee0 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -50,25 +50,31 @@ Item { view.destroy(); } - function createViewForObject(obj) + function createViewForObject(obj, env, envValue, model) { + backgroundView3d.visible = true; if (obj instanceof Material) - createViewForMaterial(obj); + createViewForMaterial(obj, env, envValue, model); else if (obj instanceof Model) createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); } - function createViewForMaterial(material) + function createViewForMaterial(material, env, envValue, model) { if (!materialViewComponent) materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); // Always recreate the view to ensure material is up to date - if (materialViewComponent.status === Component.Ready) - view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); - + if (materialViewComponent.status === Component.Ready) { + view = materialViewComponent.createObject(viewRect, {"previewMaterial": material, + "envMode": env, + "envValue": envValue, + "modelSrc": model}); + } + // Floor must be in same view as material so material can reflect it, so hide it in this one + backgroundView3d.visible = false; previewObject = material; } @@ -129,6 +135,7 @@ Item { // Use View3D instead of static image to make background look good on all resolutions View3D { + id: backgroundView3d anchors.fill: parent environment: sceneEnv diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 516213588c4..365aca3cd3b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -339,6 +339,31 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() #endif } +void Qt5InformationNodeInstanceServer::updateMaterialPreviewData(const QVector &valueChanges) +{ + const PropertyName matPrevPrefix("matPrev"); + qint32 materialLibraryId = -1; + for (const auto &container : valueChanges) { + if (container.name().startsWith(matPrevPrefix)) { + if (!hasInstanceForId(container.instanceId())) + continue; + if (materialLibraryId < 0) { + ServerNodeInstance instance = instanceForId(container.instanceId()); + if (instance.id() == "__materialLibrary__") + materialLibraryId = container.instanceId(); + } + if (container.instanceId() == materialLibraryId) { + if (container.name() == "matPrevEnv") + m_materialPreviewData.env = container.value().toString(); + else if (container.name() == "matPrevEnvValue") + m_materialPreviewData.envValue = container.value().toString(); + else if (container.name() == "matPrevModel") + m_materialPreviewData.model = container.value().toString(); + } + } + } +} + void Qt5InformationNodeInstanceServer::updateRotationBlocks(const QVector &valueChanges) { #ifdef QUICK3D_MODULE @@ -1184,7 +1209,10 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView(const Reques } else { QMetaObject::invokeMethod( m_modelNode3DImageViewData.rootItem, "createViewForObject", - Q_ARG(QVariant, objectToVariant(instanceObj))); + Q_ARG(QVariant, objectToVariant(instanceObj)), + Q_ARG(QVariant, m_materialPreviewData.env), + Q_ARG(QVariant, m_materialPreviewData.envValue), + Q_ARG(QVariant, m_materialPreviewData.model)); } // Need to render twice, first render updates spatial nodes @@ -2027,6 +2055,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com if (ViewConfig::isQuick3DMode()) { setup3DEditView(instanceList, command); updateRotationBlocks(command.auxiliaryChanges); + updateMaterialPreviewData(command.auxiliaryChanges); } QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, @@ -2431,6 +2460,7 @@ void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const Reques void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) { updateRotationBlocks(command.auxiliaryChanges); + updateMaterialPreviewData(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index f3448de4add..ea104442a6e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -145,6 +145,7 @@ private: void updateLockedAndHiddenStates(const QSet &instances); void handleInputEvents(); void resolveImportSupport(); + void updateMaterialPreviewData(const QVector &valueChanges); void updateRotationBlocks(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); @@ -191,6 +192,13 @@ private: QObject *m_3dHelper = nullptr; int m_need3DEditViewRender = 0; QSet m_dynamicObjectConstructors; + + struct PreviewData { + QString env; + QString envValue; + QString model; + }; + PreviewData m_materialPreviewData; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index a9e272b6d51..50b5ce6a7e8 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml @@ -33,6 +33,12 @@ PropertyEditorPane { id: root signal toolBarAction(int action) + signal previewEnvChanged(string env) + signal previewModelChanged(string model) + + // Called from C++, dummy methods to avoid warnings + function closeContextMenu() {} + function initPreviewData(env, model) {} Column { id: col diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index cfc037bc24f..67b5042c492 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -31,6 +31,8 @@ PropertyEditorPane { id: itemPane signal toolBarAction(int action) + signal previewEnvChanged(string env) + signal previewModelChanged(string model) // invoked from C++ to refresh material preview image function refreshPreview() @@ -38,10 +40,25 @@ PropertyEditorPane { topSection.refreshPreview() } + // Called also from C++ to close context menu on focus out + function closeContextMenu() + { + topSection.closeContextMenu() + } + + // Called from C++ to initialize preview menu checkmarks + function initPreviewData(env, model) + { + topSection.previewEnv = env; + topSection.previewModel = model + } + MaterialEditorTopSection { id: topSection onToolBarAction: (action) => itemPane.toolBarAction(action) + onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv) + onPreviewModelChanged: itemPane.previewModelChanged(previewModel) } Item { width: 1; height: 10 } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index 2e21d5814ac..0a5bcfab0d9 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -29,6 +29,7 @@ import QtQuick.Layouts 1.15 import QtQuickDesignerTheme 1.0 import QtQuick.Templates 2.15 as T import HelperWidgets 2.0 +import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme Column { @@ -36,12 +37,22 @@ Column { signal toolBarAction(int action) + property string previewEnv + property string previewModel + function refreshPreview() { materialPreview.source = "" materialPreview.source = "image://materialEditor/preview" } + // Called from C++ to close context menu on focus out + function closeContextMenu() + { + modelMenu.close() + envMenu.close() + } + anchors.left: parent.left anchors.right: parent.right @@ -53,20 +64,135 @@ Column { Item { width: 1; height: 10 } // spacer - Rectangle { - width: 152 - height: 152 - color: "#000000" - anchors.horizontalCenter: parent.horizontalCenter - Image { - id: materialPreview - width: 150 - height: 150 - anchors.centerIn: parent - source: "image://materialEditor/preview" - cache: false + StudioControls.Menu { + id: modelMenu + closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside + + ListModel { + id: modelMenuModel + ListElement { + modelName: qsTr("Cone") + modelStr: "#Cone" + } + ListElement { + modelName: qsTr("Cube") + modelStr: "#Cube" + } + ListElement { + modelName: qsTr("Cylinder") + modelStr: "#Cylinder" + } + ListElement { + modelName: qsTr("Sphere") + modelStr: "#Sphere" + } } + + Repeater { + model: modelMenuModel + StudioControls.MenuItemWithIcon { + text: modelName + onClicked: { + // Force property change notifications to keep check mark when reselected + root.previewModel = "" + root.previewModel = modelStr + } + checkable: true + checked: root.previewModel === modelStr + } + } + } + + StudioControls.Menu { + id: envMenu + closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside + + ListModel { + id: envMenuModel + ListElement { + envName: qsTr("Default") + envStr: "Default" + } + ListElement { + envName: qsTr("Color") + envStr: "Color" + } + ListElement { + envName: qsTr("Studio") + envStr: "SkyBox=preview_studio" + } + ListElement { + envName: qsTr("Landscape") + envStr: "SkyBox=preview_landscape" + } + } + + Repeater { + model: envMenuModel + StudioControls.MenuItemWithIcon { + text: envName + onClicked: { + // Force property change notifications to keep check mark when reselected + root.previewEnv = "" + root.previewEnv = envStr + } + checkable: true + checked: root.previewEnv === envStr + } + } + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: previewRect.height + + Rectangle { + id: previewRect + anchors.horizontalCenter: parent.horizontalCenter + width: 152 + height: 152 + color: "#000000" + + Image { + id: materialPreview + width: 150 + height: 150 + anchors.centerIn: parent + source: "image://materialEditor/preview" + cache: false + } + } + + Item { + id: previewOptions + width: 40 + height: previewRect.height + anchors.top: previewRect.top + anchors.left: previewRect.right + + Column { + anchors.horizontalCenter: parent.horizontalCenter + IconButton { + icon: StudioTheme.Constants.materialPreviewEnvironment + normalColor: "transparent" + iconSize: StudioTheme.Values.bigIconFontSize + buttonSize: previewOptions.width + tooltip: qsTr("Select preview environment.") + onClicked: envMenu.popup() + } + IconButton { + icon: StudioTheme.Constants.materialPreviewModel + normalColor: "transparent" + iconSize: StudioTheme.Values.bigIconFontSize + buttonSize: previewOptions.width + tooltip: qsTr("Select preview model.") + onClicked: modelMenu.popup() + } + } + } + } Section { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 5972e56e04c..46b3679198c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -122,72 +122,74 @@ QtObject { readonly property string listView: "\u0075" readonly property string lockOff: "\u0076" readonly property string lockOn: "\u0077" - readonly property string mergeCells: "\u0078" - readonly property string minus: "\u0079" - readonly property string mirror: "\u007A" - readonly property string newMaterial: "\u007B" - readonly property string openMaterialBrowser: "\u007C" - readonly property string orientation: "\u007D" - readonly property string paddingEdge: "\u007E" - readonly property string paddingFrame: "\u007F" - readonly property string pasteStyle: "\u0080" - readonly property string pause: "\u0081" - readonly property string pin: "\u0082" - readonly property string play: "\u0083" - readonly property string plus: "\u0084" - readonly property string promote: "\u0085" - readonly property string readOnly: "\u0086" - readonly property string redo: "\u0087" - readonly property string rotationFill: "\u0088" - readonly property string rotationOutline: "\u0089" - readonly property string search: "\u008A" - readonly property string sectionToggle: "\u008B" - readonly property string splitColumns: "\u008C" - readonly property string splitRows: "\u008D" - readonly property string startNode: "\u008E" - readonly property string testIcon: "\u008F" - readonly property string textAlignBottom: "\u0090" - readonly property string textAlignCenter: "\u0091" - readonly property string textAlignJustified: "\u0092" - readonly property string textAlignLeft: "\u0093" - readonly property string textAlignMiddle: "\u0094" - readonly property string textAlignRight: "\u0095" - readonly property string textAlignTop: "\u0096" - readonly property string textBulletList: "\u0097" - readonly property string textFullJustification: "\u0098" - readonly property string textNumberedList: "\u0099" - readonly property string tickIcon: "\u009A" - readonly property string translationCreateFiles: "\u009B" - readonly property string translationCreateReport: "\u009D" - readonly property string translationExport: "\u009E" - readonly property string translationImport: "\u009F" - readonly property string translationSelectLanguages: "\u00A0" - readonly property string translationTest: "\u00A1" - readonly property string transparent: "\u00A2" - readonly property string triState: "\u00A3" - readonly property string triangleArcA: "\u00A4" - readonly property string triangleArcB: "\u00A5" - readonly property string triangleCornerA: "\u00A6" - readonly property string triangleCornerB: "\u00A7" - readonly property string unLinked: "\u00A8" - readonly property string undo: "\u00A9" - readonly property string unpin: "\u00AA" - readonly property string upDownIcon: "\u00AB" - readonly property string upDownSquare2: "\u00AC" - readonly property string visibilityOff: "\u00AE" - readonly property string visibilityOn: "\u00AF" - readonly property string wildcard: "\u00B0" - readonly property string wizardsAutomotive: "\u00B1" - readonly property string wizardsDesktop: "\u00B2" - readonly property string wizardsGeneric: "\u00B3" - readonly property string wizardsMcuEmpty: "\u00B4" - readonly property string wizardsMcuGraph: "\u00B5" - readonly property string wizardsMobile: "\u00B6" - readonly property string wizardsUnknown: "\u00B7" - readonly property string zoomAll: "\u00B8" - readonly property string zoomIn: "\u00B9" - readonly property string zoomOut: "\u00BA" - readonly property string zoomSelection: "\u00BB" + readonly property string materialPreviewEnvironment: "\u0078" + readonly property string materialPreviewModel: "\u0079" + readonly property string mergeCells: "\u007A" + readonly property string minus: "\u007B" + readonly property string mirror: "\u007C" + readonly property string newMaterial: "\u007D" + readonly property string openMaterialBrowser: "\u007E" + readonly property string orientation: "\u007F" + readonly property string paddingEdge: "\u0080" + readonly property string paddingFrame: "\u0081" + readonly property string pasteStyle: "\u0082" + readonly property string pause: "\u0083" + readonly property string pin: "\u0084" + readonly property string play: "\u0085" + readonly property string plus: "\u0086" + readonly property string promote: "\u0087" + readonly property string readOnly: "\u0088" + readonly property string redo: "\u0089" + readonly property string rotationFill: "\u008A" + readonly property string rotationOutline: "\u008B" + readonly property string search: "\u008C" + readonly property string sectionToggle: "\u008D" + readonly property string splitColumns: "\u008E" + readonly property string splitRows: "\u008F" + readonly property string startNode: "\u0090" + readonly property string testIcon: "\u0091" + readonly property string textAlignBottom: "\u0092" + readonly property string textAlignCenter: "\u0093" + readonly property string textAlignJustified: "\u0094" + readonly property string textAlignLeft: "\u0095" + readonly property string textAlignMiddle: "\u0096" + readonly property string textAlignRight: "\u0097" + readonly property string textAlignTop: "\u0098" + readonly property string textBulletList: "\u0099" + readonly property string textFullJustification: "\u009A" + readonly property string textNumberedList: "\u009B" + readonly property string tickIcon: "\u009D" + readonly property string translationCreateFiles: "\u009E" + readonly property string translationCreateReport: "\u009F" + readonly property string translationExport: "\u00A0" + readonly property string translationImport: "\u00A1" + readonly property string translationSelectLanguages: "\u00A2" + readonly property string translationTest: "\u00A3" + readonly property string transparent: "\u00A4" + readonly property string triState: "\u00A5" + readonly property string triangleArcA: "\u00A6" + readonly property string triangleArcB: "\u00A7" + readonly property string triangleCornerA: "\u00A8" + readonly property string triangleCornerB: "\u00A9" + readonly property string unLinked: "\u00AA" + readonly property string undo: "\u00AB" + readonly property string unpin: "\u00AC" + readonly property string upDownIcon: "\u00AE" + readonly property string upDownSquare2: "\u00AF" + readonly property string visibilityOff: "\u00B0" + readonly property string visibilityOn: "\u00B1" + readonly property string wildcard: "\u00B2" + readonly property string wizardsAutomotive: "\u00B3" + readonly property string wizardsDesktop: "\u00B4" + readonly property string wizardsGeneric: "\u00B5" + readonly property string wizardsMcuEmpty: "\u00B6" + readonly property string wizardsMcuGraph: "\u00B7" + readonly property string wizardsMobile: "\u00B8" + readonly property string wizardsUnknown: "\u00B9" + readonly property string zoomAll: "\u00BA" + readonly property string zoomIn: "\u00BB" + readonly property string zoomOut: "\u00BC" + readonly property string zoomSelection: "\u00BD" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index cefd1d7d866..934c22110e5 100644 Binary files a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf and b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf differ diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index d8853cc7906..7707cdeb12b 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -130,6 +130,8 @@ public: listView, lockOff, lockOn, + materialPreviewEnvironment, + materialPreviewModel, mergeCells, minus, mirror, diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 7684e4fe1bc..ae04c193ca9 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -284,6 +284,10 @@ void MaterialBrowserView::customNotification(const AbstractView *view, const QSt int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first()); if (idx != -1) m_widget->materialBrowserModel()->selectMaterial(idx); + } else if (identifier == "refresh_material_browser") { + QTimer::singleShot(0, this, [this]() { + refreshModel(true); + }); } } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 974435b5104..c0b766648b6 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -62,6 +62,7 @@ #include #include #include +#include namespace QmlDesigner { @@ -442,6 +443,90 @@ void MaterialEditorView::handleToolBarAction(int action) } } +void MaterialEditorView::handlePreviewEnvChanged(const QString &envAndValue) +{ + if (envAndValue.isEmpty()) + return; + + QTC_ASSERT(m_hasQuick3DImport, return); + QTC_ASSERT(model(), return); + QTC_ASSERT(model()->nodeInstanceView(), return); + + QStringList parts = envAndValue.split('='); + QString env = parts[0]; + QString value; + if (parts.size() > 1) + value = parts[1]; + + PropertyName matPrevEnvAuxProp("matPrevEnv"); + PropertyName matPrevEnvValueAuxProp("matPrevEnvValue"); + + auto renderPreviews = [=](const QString &auxEnv, const QString &auxValue) { + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + QTC_ASSERT(matLib.isValid(), return); + matLib.setAuxiliaryData(matPrevEnvAuxProp, auxEnv); + matLib.setAuxiliaryData(matPrevEnvValueAuxProp, auxValue); + QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); + emitCustomNotification("refresh_material_browser", {}); + }; + + if (env == "Color") { + m_colorDialog.clear(); + + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + QTC_ASSERT(matLib.isValid(), return); + // Store color to separate property to persist selection over non-color env changes + PropertyName colorAuxProp("matPrevColor"); + QString oldColor = matLib.auxiliaryData(colorAuxProp).toString(); + QString oldEnv = matLib.auxiliaryData(matPrevEnvAuxProp).toString(); + QString oldValue = matLib.auxiliaryData(matPrevEnvValueAuxProp).toString(); + + m_colorDialog = new QColorDialog(Core::ICore::dialogParent()); + m_colorDialog->setModal(true); + m_colorDialog->setAttribute(Qt::WA_DeleteOnClose); + m_colorDialog->setCurrentColor(QColor(oldColor)); + m_colorDialog->show(); + + QObject::connect(m_colorDialog, &QColorDialog::currentColorChanged, + m_colorDialog, [=](const QColor &color) { + renderPreviews(env, color.name()); + }); + + QObject::connect(m_colorDialog, &QColorDialog::colorSelected, + m_colorDialog, [=](const QColor &color) { + renderPreviews(env, color.name()); + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + QTC_ASSERT(matLib.isValid(), return); + matLib.setAuxiliaryData(colorAuxProp, color.name()); + }); + + QObject::connect(m_colorDialog, &QColorDialog::rejected, + m_colorDialog, [=]() { + renderPreviews(oldEnv, oldValue); + initPreviewData(); + }); + return; + } + renderPreviews(env, value); +} + +void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr) +{ + if (modelStr.isEmpty()) + return; + + QTC_ASSERT(m_hasQuick3DImport, return); + QTC_ASSERT(model(), return); + QTC_ASSERT(model()->nodeInstanceView(), return); + + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + QTC_ASSERT(matLib.isValid(), return); + matLib.setAuxiliaryData("matPrevModel", modelStr); + + QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); + emitCustomNotification("refresh_material_browser", {}); +} + void MaterialEditorView::setupQmlBackend() { QUrl qmlPaneUrl; @@ -472,17 +557,24 @@ void MaterialEditorView::setupQmlBackend() currentQmlBackend->setSource(qmlPaneUrl); - QObject::connect(currentQmlBackend->widget()->rootObject(), SIGNAL(toolBarAction(int)), + QObject *rootObj = currentQmlBackend->widget()->rootObject(); + QObject::connect(rootObj, SIGNAL(toolBarAction(int)), this, SLOT(handleToolBarAction(int))); + QObject::connect(rootObj, SIGNAL(previewEnvChanged(QString)), + this, SLOT(handlePreviewEnvChanged(QString))); + QObject::connect(rootObj, SIGNAL(previewModelChanged(QString)), + this, SLOT(handlePreviewModelChanged(QString))); } else { currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this); } + currentQmlBackend->widget()->installEventFilter(this); currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); m_stackedWidget->setCurrentWidget(currentQmlBackend->widget()); m_qmlBackEnd = currentQmlBackend; + initPreviewData(); } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -528,6 +620,29 @@ bool MaterialEditorView::noValidSelection() const return !QmlObjectNode::isValidQmlObjectNode(m_selectedMaterial); } +void MaterialEditorView::initPreviewData() +{ + if (model() && m_qmlBackEnd) { + ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); + if (matLib.isValid()) { + QString env = matLib.auxiliaryData("matPrevEnv").toString(); + QString envValue = matLib.auxiliaryData("matPrevEnvValue").toString(); + QString modelStr = matLib.auxiliaryData("matPrevModel").toString(); + if (!envValue.isEmpty() && env != "Color" && env != "Default") { + env += '='; + env += envValue; + } + if (env.isEmpty()) + env = "Default"; + if (modelStr.isEmpty()) + modelStr = "#Sphere"; + QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), + "initPreviewData", + Q_ARG(QVariant, env), Q_ARG(QVariant, modelStr)); + } + } +} + void MaterialEditorView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -844,6 +959,15 @@ void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const Prop m_locked = false; } +bool MaterialEditorView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::FocusOut) { + if (m_qmlBackEnd && m_qmlBackEnd->widget() == obj) + QMetaObject::invokeMethod(m_qmlBackEnd->widget()->rootObject(), "closeContextMenu"); + } + return QObject::eventFilter(obj, event); +} + void MaterialEditorView::reloadQml() { m_qmlBackendHash.clear(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index ff734ed30b8..1c52be87cfa 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -27,12 +27,14 @@ #include #include +#include #include QT_BEGIN_NAMESPACE class QShortcut; class QStackedWidget; class QTimer; +class QColorDialog; QT_END_NAMESPACE namespace QmlDesigner { @@ -87,10 +89,13 @@ public: public slots: void handleToolBarAction(int action); + void handlePreviewEnvChanged(const QString &envAndValue); + void handlePreviewModelChanged(const QString &modelStr); protected: void timerEvent(QTimerEvent *event) override; void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value); + bool eventFilter(QObject *obj, QEvent *event) override; private: static QString materialEditorResourcesPath(); @@ -113,6 +118,8 @@ private: bool noValidSelection() const; + void initPreviewData(); + ModelNode m_selectedMaterial; QTimer m_ensureMatLibTimer; QShortcut *m_updateShortcut = nullptr; @@ -124,6 +131,8 @@ private: bool m_locked = false; bool m_setupCompleted = false; bool m_hasQuick3DImport = false; + + QPointer m_colorDialog; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 4522c14a54f..e84f9224d61 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -660,7 +660,8 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, const QVariant &value) { QTC_ASSERT(m_nodeInstanceServer, return); - const bool forceAuxChange = name == "invisible" || name == "locked" || name == "rotBlocked@Internal"; + const bool forceAuxChange = name == "invisible" || name == "locked" + || name == "rotBlocked@Internal" || name.startsWith("matPrev"); if (((node.isRootNode() && (name == "width" || name == "height")) || forceAuxChange) || name.endsWith(PropertyName("@NodeInstance"))) { if (hasInstanceForModelNode(node)) {