diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index dee83071924..5010017f2b0 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -2,12 +2,17 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Controls import QtCore import HelperWidgets +import StudioControls 1.0 as StudioControls Item { id: root + property string __previewEnv + property string __previewModel + width: 420 height: 420 @@ -16,23 +21,18 @@ Item { signal previewModelChanged(string model) // invoked from C++ to refresh material preview image - function refreshPreview() - { - itemPane.headerItem.refreshPreview() - } + signal refreshPreview() // Called from C++ to close context menu on focus out function closeContextMenu() { - itemPane.headerItem.closeContextMenu() Controller.closeContextMenu() } // Called from C++ to initialize preview menu checkmarks - function initPreviewData(env, model) - { - itemPane.headerItem.previewEnv = env - itemPane.headerItem.previewModel = model + function initPreviewData(env, model) { + root.__previewEnv = env + root.__previewModel = model } MaterialEditorToolBar { @@ -47,56 +47,105 @@ Item { id: settings property var topSection + property bool dockMode } - PropertyEditorPane { - id: itemPane + StudioControls.SplitView { + id: splitView + + readonly property bool isHorizontal: splitView.orientation == Qt.Horizontal anchors.top: toolbar.bottom anchors.bottom: parent.bottom width: parent.width - + orientation: splitView.width > 1000 ? Qt.Horizontal : Qt.Vertical clip: true - headerComponent: MaterialEditorTopSection { - id: topSection - onPreviewEnvChanged: root.previewEnvChanged(previewEnv) - onPreviewModelChanged: root.previewModelChanged(previewModel) - - Component.onCompleted: topSection.restoreState(settings.topSection) - Component.onDestruction: settings.topSection = topSection.saveState() - } - - DynamicPropertiesSection { - propertiesModel: MaterialEditorDynamicPropertiesModel {} - } - Loader { - id: specificsTwo + id: leftSideView - property string theSource: specificQmlData + SplitView.fillWidth: leftSideView.visible + SplitView.fillHeight: leftSideView.visible + SplitView.minimumWidth: leftSideView.visible ? 300 : 0 + SplitView.minimumHeight: leftSideView.visible ? 300 : 0 - width: parent.width - visible: specificsTwo.theSource !== "" - sourceComponent: specificQmlComponent + active: splitView.isHorizontal + visible: leftSideView.active && leftSideView.item - onTheSourceChanged: { - specificsTwo.active = false - specificsTwo.active = true + sourceComponent: PreviewComponent {} + } + + PropertyEditorPane { + id: itemPane + + clip: true + SplitView.fillWidth: !leftSideView.visible + SplitView.fillHeight: true + SplitView.minimumWidth: leftSideView.visible ? 400 : 0 + SplitView.maximumWidth: leftSideView.visible ? 800 : -1 + + headerDocked: !leftSideView.visible && settings.dockMode + + headerComponent: MaterialEditorTopSection { + id: topSection + + Component.onCompleted: topSection.restoreState(settings.topSection) + Component.onDestruction: settings.topSection = topSection.saveState() + previewComponent: PreviewComponent {} + showImage: !splitView.isHorizontal + } + + DynamicPropertiesSection { + propertiesModel: MaterialEditorDynamicPropertiesModel {} + } + + Loader { + id: specificsTwo + + property string theSource: specificQmlData + + width: itemPane.width + visible: specificsTwo.theSource !== "" + sourceComponent: specificQmlComponent + + onTheSourceChanged: { + specificsTwo.active = false + specificsTwo.active = true + } + } + + Item { // spacer + width: 1 + height: 10 + visible: specificsTwo.visible + } + + Loader { + id: specificsOne + width: itemPane.width + source: specificsUrl } } + } - Item { // spacer - width: 1 - height: 10 - visible: specificsTwo.visible - } + component PreviewComponent : MaterialEditorPreview { + id: previewItem - Loader { - id: specificsOne - anchors.left: parent.left - anchors.right: parent.right - source: specificsUrl + onPreviewEnvChanged: root.previewEnvChanged(previewEnv) + onPreviewModelChanged: root.previewModelChanged(previewModel) + + previewEnv: root.__previewEnv + previewModel: root.__previewModel + pinned: settings.dockMode + showPinButton: !leftSideView.visible + onPinnedChanged: settings.dockMode = previewItem.pinned + + Connections { + target: root + + function onRefreshPreview() { + previewItem.refreshPreview() + } } } } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPreview.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPreview.qml new file mode 100644 index 00000000000..fe95e33d900 --- /dev/null +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPreview.qml @@ -0,0 +1,185 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property string previewEnv + property string previewModel + property alias pinned: pinButton.checked + property alias showPinButton: pinButton.visible + + property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { + // This is how you can override stuff from the control styles + baseIconFontSize: StudioTheme.Values.bigIconFontSize + } + + Connections { + target: HelperWidgets.Controller + + function onCloseContextMenu() { + root.closeContextMenu() + } + } + + implicitHeight: image.height + + clip: true + color: "#000000" + + // Called from C++ to close context menu on focus out + function closeContextMenu() + { + modelMenu.close() + envMenu.close() + } + + function refreshPreview() + { + image.source = "" + image.source = "image://materialEditor/preview" + } + + Image { + id: image + + anchors.fill: parent + fillMode: Image.PreserveAspectFit + + source: "image://materialEditor/preview" + cache: false + smooth: true + + sourceSize.width: image.width + sourceSize.height: image.height + + Rectangle { + id: toolbarRect + + radius: 10 + color: StudioTheme.Values.themeToolbarBackground + width: optionsToolbar.width + 2 * toolbarRect.radius + height: optionsToolbar.height + toolbarRect.radius + anchors.left: parent.left + anchors.leftMargin: -toolbarRect.radius + anchors.verticalCenter: parent.verticalCenter + + Column { + id: optionsToolbar + + spacing: 5 + anchors.centerIn: parent + anchors.horizontalCenterOffset: optionsToolbar.spacing + + HelperWidgets.AbstractButton { + id: pinButton + + style: root.buttonStyle + buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin + checkable: true + } + + HelperWidgets.AbstractButton { + id: previewEnvMenuButton + + style: root.buttonStyle + buttonIcon: StudioTheme.Constants.textures_medium + tooltip: qsTr("Select preview environment.") + onClicked: envMenu.popup() + } + + HelperWidgets.AbstractButton { + id: previewModelMenuButton + + style: root.buttonStyle + buttonIcon: StudioTheme.Constants.cube_medium + tooltip: qsTr("Select preview model.") + onClicked: modelMenu.popup() + } + } + } + } + + 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("Basic") + envStr: "Basic" + } + 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 + } + } + } +} diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index e61fb8df354..e4aa7515635 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -8,201 +8,29 @@ import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme -SplitView { +StudioControls.SplitView { id: root - property string previewEnv - property string previewModel + property alias showImage: previewLoader.active + property Component previewComponent: null - property real __spacing: 5 - - property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { - //This is how you can override stuff from the control styles - baseIconFontSize: StudioTheme.Values.bigIconFontSize - } - - 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 - - implicitHeight: previewRect.implicitHeight + nameSection.implicitHeight + width: parent.width + implicitHeight: showImage ? previewLoader.implicitHeight + nameSection.implicitHeight : nameSection.implicitHeight orientation: Qt.Vertical - handle: Rectangle { - implicitWidth: root.orientation === Qt.Horizontal ? StudioTheme.Values.splitterThickness : root.width - implicitHeight: root.orientation === Qt.Horizontal ? root.height : StudioTheme.Values.splitterThickness - color: SplitHandle.pressed ? StudioTheme.Values.themeSliderHandleInteraction - : (SplitHandle.hovered ? StudioTheme.Values.themeSliderHandleHover - : "transparent") - } - - 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("Basic") - envStr: "Basic" - } - 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 - } - } - } - - Rectangle { - id: previewRect + Loader { + id: previewLoader SplitView.fillWidth: true SplitView.minimumWidth: 152 - SplitView.preferredHeight: Math.min(root.width * 0.75, 400) - SplitView.minimumHeight: 150 - implicitHeight: materialPreview.height + SplitView.preferredHeight: previewLoader.visible ? Math.min(root.width * 0.75, 400) : 0 + SplitView.minimumHeight: previewLoader.visible ? 150 : 0 + SplitView.maximumHeight: previewLoader.visible ? 600 : 0 - clip: true - color: "#000000" + visible: previewLoader.active && previewLoader.item - Image { - id: materialPreview - - width: root.width - height: previewRect.height - anchors.centerIn: parent - - fillMode: Image.PreserveAspectFit - - source: "image://materialEditor/preview" - cache: false - smooth: true - - sourceSize.width: materialPreview.width - sourceSize.height: materialPreview.height - - Rectangle { - id: toolbarRect - - radius: 10 - color: StudioTheme.Values.themeToolbarBackground - width: optionsToolbar.width + 2 * toolbarRect.radius - height: optionsToolbar.height + toolbarRect.radius - anchors.left: parent.left - anchors.leftMargin: -toolbarRect.radius - anchors.verticalCenter: parent.verticalCenter - - Column { - id: optionsToolbar - - spacing: root.__spacing - anchors.centerIn: parent - anchors.horizontalCenterOffset: root.__spacing - - HelperWidgets.AbstractButton { - id: pinButton - - style: buttonStyle - buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin - checkable: true - checked: itemPane.headerDocked - onCheckedChanged: itemPane.headerDocked = pinButton.checked - } - - HelperWidgets.AbstractButton { - id: previewEnvMenuButton - - style: buttonStyle - buttonIcon: StudioTheme.Constants.textures_medium - tooltip: qsTr("Select preview environment.") - onClicked: envMenu.popup() - } - - HelperWidgets.AbstractButton { - id: previewModelMenuButton - - style: buttonStyle - buttonIcon: StudioTheme.Constants.cube_medium - tooltip: qsTr("Select preview model.") - onClicked: modelMenu.popup() - } - } - } - } + sourceComponent: root.previewComponent } HelperWidgets.Section { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SplitView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SplitView.qml new file mode 100644 index 00000000000..4c7fb5af0d5 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SplitView.qml @@ -0,0 +1,20 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls as QuickControls +import StudioTheme 1.0 as StudioTheme + +QuickControls.SplitView { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + handle: Rectangle { + implicitWidth: control.orientation === Qt.Horizontal ? StudioTheme.Values.splitterThickness : control.width + implicitHeight: control.orientation === Qt.Horizontal ? control.height : StudioTheme.Values.splitterThickness + color: QuickControls.SplitHandle.pressed ? control.style.slider.handleInteraction + : (QuickControls.SplitHandle.hovered ? control.style.slider.handleHover + : "transparent") + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index e93f6f59ded..4266910554f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -51,6 +51,7 @@ SortFilterModel 1.0 SortFilterModel.qml SpinBox 1.0 SpinBox.qml SpinBoxIndicator 1.0 SpinBoxIndicator.qml SpinBoxInput 1.0 SpinBoxInput.qml +SplitView 1.0 SplitView.qml Switch 1.0 Switch.qml TabBar 1.0 TabBar.qml TabButton 1.0 TabButton.qml diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index e59581b3ac9..f24bda1068a 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -863,10 +863,13 @@ void MaterialEditorView::propertiesAboutToBeRemoved(const QListnodeInstanceView() && m_selectedMaterial.isValid()) { + static int requestId = 0; + m_previewRequestId = QByteArray(MATERIAL_EDITOR_IMAGE_REQUEST_ID) + + QByteArray::number(++requestId); model()->nodeInstanceView()->previewImageDataForGenericNode(m_selectedMaterial, {}, m_previewSize, - MATERIAL_EDITOR_IMAGE_REQUEST_ID); + m_previewRequestId); } } @@ -955,11 +958,10 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap, const QByteArray &requestId) { - if (node != m_selectedMaterial) + if (node != m_selectedMaterial || requestId != m_previewRequestId) return; - if (requestId == MATERIAL_EDITOR_IMAGE_REQUEST_ID) - m_qmlBackEnd->updateMaterialPreview(pixmap); + m_qmlBackEnd->updateMaterialPreview(pixmap); } void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index 7ef52dd3be5..e35e90861bb 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -129,6 +129,7 @@ private: bool m_hasMaterialRoot = false; bool m_initializingPreviewData = false; QSize m_previewSize; + QByteArray m_previewRequestId; QPointer m_colorDialog; QPointer m_itemLibraryInfo;