QmlDesigner: Add environment and model selectors to material preview

The model and the scene environment used to render material previews
can now be selected via buttons next to the preview in material editor.

Task-number: QDS-7347
Change-Id: I03089029e8420f80ed65be1c7b7a1ce4581f2fd4
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Miikka Heikkinen
2022-08-05 17:14:05 +03:00
parent 774010d96e
commit 45f93a817a
20 changed files with 543 additions and 112 deletions

View File

@@ -16,6 +16,8 @@
<file>mockfiles/images/static_floor.png</file> <file>mockfiles/images/static_floor.png</file>
<file>mockfiles/images/spot.png</file> <file>mockfiles/images/spot.png</file>
<file>mockfiles/images/spot@2x.png</file> <file>mockfiles/images/spot@2x.png</file>
<file>mockfiles/images/preview_landscape.hdr</file>
<file>mockfiles/images/preview_studio.hdr</file>
<file>mockfiles/qt5/AdjustableArrow.qml</file> <file>mockfiles/qt5/AdjustableArrow.qml</file>
<file>mockfiles/qt5/AreaLightHandle.qml</file> <file>mockfiles/qt5/AreaLightHandle.qml</file>
<file>mockfiles/qt5/Arrow.qml</file> <file>mockfiles/qt5/Arrow.qml</file>

View File

@@ -18,6 +18,8 @@
<file>mockfiles/images/floor_tex.png</file> <file>mockfiles/images/floor_tex.png</file>
<file>mockfiles/images/spot.png</file> <file>mockfiles/images/spot.png</file>
<file>mockfiles/images/spot@2x.png</file> <file>mockfiles/images/spot@2x.png</file>
<file>mockfiles/images/preview_landscape.hdr</file>
<file>mockfiles/images/preview_studio.hdr</file>
<file>mockfiles/qt6/AdjustableArrow.qml</file> <file>mockfiles/qt6/AdjustableArrow.qml</file>
<file>mockfiles/qt6/AreaLightHandle.qml</file> <file>mockfiles/qt6/AreaLightHandle.qml</file>
<file>mockfiles/qt6/Arrow.qml</file> <file>mockfiles/qt6/Arrow.qml</file>

View File

@@ -29,8 +29,12 @@ View3D {
id: root id: root
anchors.fill: parent anchors.fill: parent
environment: sceneEnv environment: sceneEnv
camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera
property Material previewMaterial property Material previewMaterial
property string envMode
property string envValue
property string modelSrc: "#Sphere"
function fitToViewPort(closeUp) function fitToViewPort(closeUp)
{ {
@@ -41,28 +45,59 @@ View3D {
id: sceneEnv id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High 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 { Node {
DirectionalLight { DirectionalLight {
eulerRotation.x: -26 eulerRotation.x: -26
eulerRotation.y: -57 eulerRotation.y: modelSrc === "#Cube" ? -10 : -50
brightness: envMode !== "SkyBox" ? 100 : 0
} }
PerspectiveCamera { PerspectiveCamera {
y: 125.331 id: defaultCamera
z: 120 y: 70
eulerRotation.x: -31 z: 200
eulerRotation.x: -5.71
clipNear: 1 clipNear: 1
clipFar: 1000 clipFar: 1000
} }
Model { PerspectiveCamera {
id: model id: studioCamera
y: 232
z: 85
eulerRotation.x: -64.98
clipNear: 1
clipFar: 1000
}
Node {
rotation: root.camera.rotation
y: 50 y: 50
source: "#Sphere" Node {
materials: previewMaterial 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)
}
}
} }
} }
} }

View File

@@ -50,25 +50,28 @@ Item {
view.destroy(); view.destroy();
} }
function createViewForObject(obj) function createViewForObject(obj, env, envValue, model)
{ {
if (obj instanceof Material) if (obj instanceof Material)
createViewForMaterial(obj); createViewForMaterial(obj, env, envValue, model);
else if (obj instanceof Model) else if (obj instanceof Model)
createViewForModel(obj); createViewForModel(obj);
else if (obj instanceof Node) else if (obj instanceof Node)
createViewForNode(obj); createViewForNode(obj);
} }
function createViewForMaterial(material) function createViewForMaterial(material, env, envValue, model)
{ {
if (!materialViewComponent) if (!materialViewComponent)
materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); materialViewComponent = Qt.createComponent("MaterialNodeView.qml");
// Always recreate the view to ensure material is up to date // Always recreate the view to ensure material is up to date
if (materialViewComponent.status === Component.Ready) if (materialViewComponent.status === Component.Ready) {
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); view = materialViewComponent.createObject(viewRect, {"previewMaterial": material,
"envMode": env,
"envValue": envValue,
"modelSrc": model});
}
previewObject = material; previewObject = material;
} }

View File

@@ -29,8 +29,12 @@ View3D {
id: root id: root
anchors.fill: parent anchors.fill: parent
environment: sceneEnv environment: sceneEnv
camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera
property Material previewMaterial property Material previewMaterial
property string envMode
property string envValue
property string modelSrc: "#Sphere"
function fitToViewPort(closeUp) function fitToViewPort(closeUp)
{ {
@@ -41,30 +45,79 @@ View3D {
id: sceneEnv id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.High 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 { Node {
DirectionalLight { DirectionalLight {
eulerRotation.x: -26 eulerRotation.x: -26
eulerRotation.y: -57 eulerRotation.y: modelSrc === "#Cube" ? -10 : -50
brightness: envMode !== "SkyBox" ? 1 : 0
} }
PerspectiveCamera { PerspectiveCamera {
y: 125.331 id: defaultCamera
z: 120 y: 70
eulerRotation.x: -31 z: 200
eulerRotation.x: -5.71
clipNear: 1 clipNear: 1
clipFar: 1000 clipFar: 1000
} }
Model { PerspectiveCamera {
id: model id: studioCamera
readonly property bool _edit3dLocked: true // Make this non-pickable y: 232
z: 85
y: 50 eulerRotation.x: -64.98
source: "#Sphere" clipNear: 1
materials: previewMaterial 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
}
}
}
} }
} }

View File

@@ -50,25 +50,31 @@ Item {
view.destroy(); view.destroy();
} }
function createViewForObject(obj) function createViewForObject(obj, env, envValue, model)
{ {
backgroundView3d.visible = true;
if (obj instanceof Material) if (obj instanceof Material)
createViewForMaterial(obj); createViewForMaterial(obj, env, envValue, model);
else if (obj instanceof Model) else if (obj instanceof Model)
createViewForModel(obj); createViewForModel(obj);
else if (obj instanceof Node) else if (obj instanceof Node)
createViewForNode(obj); createViewForNode(obj);
} }
function createViewForMaterial(material) function createViewForMaterial(material, env, envValue, model)
{ {
if (!materialViewComponent) if (!materialViewComponent)
materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); materialViewComponent = Qt.createComponent("MaterialNodeView.qml");
// Always recreate the view to ensure material is up to date // Always recreate the view to ensure material is up to date
if (materialViewComponent.status === Component.Ready) if (materialViewComponent.status === Component.Ready) {
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); 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; previewObject = material;
} }
@@ -129,6 +135,7 @@ Item {
// Use View3D instead of static image to make background look good on all resolutions // Use View3D instead of static image to make background look good on all resolutions
View3D { View3D {
id: backgroundView3d
anchors.fill: parent anchors.fill: parent
environment: sceneEnv environment: sceneEnv

View File

@@ -339,6 +339,31 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport()
#endif #endif
} }
void Qt5InformationNodeInstanceServer::updateMaterialPreviewData(const QVector<PropertyValueContainer> &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<PropertyValueContainer> &valueChanges) void Qt5InformationNodeInstanceServer::updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges)
{ {
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
@@ -1184,7 +1209,10 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView(const Reques
} else { } else {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
m_modelNode3DImageViewData.rootItem, "createViewForObject", 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 // Need to render twice, first render updates spatial nodes
@@ -2027,6 +2055,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com
if (ViewConfig::isQuick3DMode()) { if (ViewConfig::isQuick3DMode()) {
setup3DEditView(instanceList, command); setup3DEditView(instanceList, command);
updateRotationBlocks(command.auxiliaryChanges); updateRotationBlocks(command.auxiliaryChanges);
updateMaterialPreviewData(command.auxiliaryChanges);
} }
QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout,
@@ -2431,6 +2460,7 @@ void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const Reques
void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{ {
updateRotationBlocks(command.auxiliaryChanges); updateRotationBlocks(command.auxiliaryChanges);
updateMaterialPreviewData(command.auxiliaryChanges);
Qt5NodeInstanceServer::changeAuxiliaryValues(command); Qt5NodeInstanceServer::changeAuxiliaryValues(command);
render3DEditView(); render3DEditView();
} }

View File

@@ -145,6 +145,7 @@ private:
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances); void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
void handleInputEvents(); void handleInputEvents();
void resolveImportSupport(); void resolveImportSupport();
void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges);
void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges); void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges);
void removeRotationBlocks(const QVector<qint32> &instanceIds); void removeRotationBlocks(const QVector<qint32> &instanceIds);
@@ -191,6 +192,13 @@ private:
QObject *m_3dHelper = nullptr; QObject *m_3dHelper = nullptr;
int m_need3DEditViewRender = 0; int m_need3DEditViewRender = 0;
QSet<QObject *> m_dynamicObjectConstructors; QSet<QObject *> m_dynamicObjectConstructors;
struct PreviewData {
QString env;
QString envValue;
QString model;
};
PreviewData m_materialPreviewData;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -33,6 +33,12 @@ PropertyEditorPane {
id: root id: root
signal toolBarAction(int action) 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 { Column {
id: col id: col

View File

@@ -31,6 +31,8 @@ PropertyEditorPane {
id: itemPane id: itemPane
signal toolBarAction(int action) signal toolBarAction(int action)
signal previewEnvChanged(string env)
signal previewModelChanged(string model)
// invoked from C++ to refresh material preview image // invoked from C++ to refresh material preview image
function refreshPreview() function refreshPreview()
@@ -38,10 +40,25 @@ PropertyEditorPane {
topSection.refreshPreview() 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 { MaterialEditorTopSection {
id: topSection id: topSection
onToolBarAction: (action) => itemPane.toolBarAction(action) onToolBarAction: (action) => itemPane.toolBarAction(action)
onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv)
onPreviewModelChanged: itemPane.previewModelChanged(previewModel)
} }
Item { width: 1; height: 10 } Item { width: 1; height: 10 }

View File

@@ -29,6 +29,7 @@ import QtQuick.Layouts 1.15
import QtQuickDesignerTheme 1.0 import QtQuickDesignerTheme 1.0
import QtQuick.Templates 2.15 as T import QtQuick.Templates 2.15 as T
import HelperWidgets 2.0 import HelperWidgets 2.0
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
Column { Column {
@@ -36,12 +37,22 @@ Column {
signal toolBarAction(int action) signal toolBarAction(int action)
property string previewEnv
property string previewModel
function refreshPreview() function refreshPreview()
{ {
materialPreview.source = "" materialPreview.source = ""
materialPreview.source = "image://materialEditor/preview" 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.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@@ -53,20 +64,135 @@ Column {
Item { width: 1; height: 10 } // spacer Item { width: 1; height: 10 } // spacer
Rectangle {
width: 152
height: 152
color: "#000000"
anchors.horizontalCenter: parent.horizontalCenter
Image { StudioControls.Menu {
id: materialPreview id: modelMenu
width: 150 closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
height: 150
anchors.centerIn: parent ListModel {
source: "image://materialEditor/preview" id: modelMenuModel
cache: false 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 { Section {

View File

@@ -122,72 +122,74 @@ QtObject {
readonly property string listView: "\u0075" readonly property string listView: "\u0075"
readonly property string lockOff: "\u0076" readonly property string lockOff: "\u0076"
readonly property string lockOn: "\u0077" readonly property string lockOn: "\u0077"
readonly property string mergeCells: "\u0078" readonly property string materialPreviewEnvironment: "\u0078"
readonly property string minus: "\u0079" readonly property string materialPreviewModel: "\u0079"
readonly property string mirror: "\u007A" readonly property string mergeCells: "\u007A"
readonly property string newMaterial: "\u007B" readonly property string minus: "\u007B"
readonly property string openMaterialBrowser: "\u007C" readonly property string mirror: "\u007C"
readonly property string orientation: "\u007D" readonly property string newMaterial: "\u007D"
readonly property string paddingEdge: "\u007E" readonly property string openMaterialBrowser: "\u007E"
readonly property string paddingFrame: "\u007F" readonly property string orientation: "\u007F"
readonly property string pasteStyle: "\u0080" readonly property string paddingEdge: "\u0080"
readonly property string pause: "\u0081" readonly property string paddingFrame: "\u0081"
readonly property string pin: "\u0082" readonly property string pasteStyle: "\u0082"
readonly property string play: "\u0083" readonly property string pause: "\u0083"
readonly property string plus: "\u0084" readonly property string pin: "\u0084"
readonly property string promote: "\u0085" readonly property string play: "\u0085"
readonly property string readOnly: "\u0086" readonly property string plus: "\u0086"
readonly property string redo: "\u0087" readonly property string promote: "\u0087"
readonly property string rotationFill: "\u0088" readonly property string readOnly: "\u0088"
readonly property string rotationOutline: "\u0089" readonly property string redo: "\u0089"
readonly property string search: "\u008A" readonly property string rotationFill: "\u008A"
readonly property string sectionToggle: "\u008B" readonly property string rotationOutline: "\u008B"
readonly property string splitColumns: "\u008C" readonly property string search: "\u008C"
readonly property string splitRows: "\u008D" readonly property string sectionToggle: "\u008D"
readonly property string startNode: "\u008E" readonly property string splitColumns: "\u008E"
readonly property string testIcon: "\u008F" readonly property string splitRows: "\u008F"
readonly property string textAlignBottom: "\u0090" readonly property string startNode: "\u0090"
readonly property string textAlignCenter: "\u0091" readonly property string testIcon: "\u0091"
readonly property string textAlignJustified: "\u0092" readonly property string textAlignBottom: "\u0092"
readonly property string textAlignLeft: "\u0093" readonly property string textAlignCenter: "\u0093"
readonly property string textAlignMiddle: "\u0094" readonly property string textAlignJustified: "\u0094"
readonly property string textAlignRight: "\u0095" readonly property string textAlignLeft: "\u0095"
readonly property string textAlignTop: "\u0096" readonly property string textAlignMiddle: "\u0096"
readonly property string textBulletList: "\u0097" readonly property string textAlignRight: "\u0097"
readonly property string textFullJustification: "\u0098" readonly property string textAlignTop: "\u0098"
readonly property string textNumberedList: "\u0099" readonly property string textBulletList: "\u0099"
readonly property string tickIcon: "\u009A" readonly property string textFullJustification: "\u009A"
readonly property string translationCreateFiles: "\u009B" readonly property string textNumberedList: "\u009B"
readonly property string translationCreateReport: "\u009D" readonly property string tickIcon: "\u009D"
readonly property string translationExport: "\u009E" readonly property string translationCreateFiles: "\u009E"
readonly property string translationImport: "\u009F" readonly property string translationCreateReport: "\u009F"
readonly property string translationSelectLanguages: "\u00A0" readonly property string translationExport: "\u00A0"
readonly property string translationTest: "\u00A1" readonly property string translationImport: "\u00A1"
readonly property string transparent: "\u00A2" readonly property string translationSelectLanguages: "\u00A2"
readonly property string triState: "\u00A3" readonly property string translationTest: "\u00A3"
readonly property string triangleArcA: "\u00A4" readonly property string transparent: "\u00A4"
readonly property string triangleArcB: "\u00A5" readonly property string triState: "\u00A5"
readonly property string triangleCornerA: "\u00A6" readonly property string triangleArcA: "\u00A6"
readonly property string triangleCornerB: "\u00A7" readonly property string triangleArcB: "\u00A7"
readonly property string unLinked: "\u00A8" readonly property string triangleCornerA: "\u00A8"
readonly property string undo: "\u00A9" readonly property string triangleCornerB: "\u00A9"
readonly property string unpin: "\u00AA" readonly property string unLinked: "\u00AA"
readonly property string upDownIcon: "\u00AB" readonly property string undo: "\u00AB"
readonly property string upDownSquare2: "\u00AC" readonly property string unpin: "\u00AC"
readonly property string visibilityOff: "\u00AE" readonly property string upDownIcon: "\u00AE"
readonly property string visibilityOn: "\u00AF" readonly property string upDownSquare2: "\u00AF"
readonly property string wildcard: "\u00B0" readonly property string visibilityOff: "\u00B0"
readonly property string wizardsAutomotive: "\u00B1" readonly property string visibilityOn: "\u00B1"
readonly property string wizardsDesktop: "\u00B2" readonly property string wildcard: "\u00B2"
readonly property string wizardsGeneric: "\u00B3" readonly property string wizardsAutomotive: "\u00B3"
readonly property string wizardsMcuEmpty: "\u00B4" readonly property string wizardsDesktop: "\u00B4"
readonly property string wizardsMcuGraph: "\u00B5" readonly property string wizardsGeneric: "\u00B5"
readonly property string wizardsMobile: "\u00B6" readonly property string wizardsMcuEmpty: "\u00B6"
readonly property string wizardsUnknown: "\u00B7" readonly property string wizardsMcuGraph: "\u00B7"
readonly property string zoomAll: "\u00B8" readonly property string wizardsMobile: "\u00B8"
readonly property string zoomIn: "\u00B9" readonly property string wizardsUnknown: "\u00B9"
readonly property string zoomOut: "\u00BA" readonly property string zoomAll: "\u00BA"
readonly property string zoomSelection: "\u00BB" readonly property string zoomIn: "\u00BB"
readonly property string zoomOut: "\u00BC"
readonly property string zoomSelection: "\u00BD"
readonly property font iconFont: Qt.font({ readonly property font iconFont: Qt.font({
"family": controlIcons.name, "family": controlIcons.name,

View File

@@ -130,6 +130,8 @@ public:
listView, listView,
lockOff, lockOff,
lockOn, lockOn,
materialPreviewEnvironment,
materialPreviewModel,
mergeCells, mergeCells,
minus, minus,
mirror, mirror,

View File

@@ -284,6 +284,10 @@ void MaterialBrowserView::customNotification(const AbstractView *view, const QSt
int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first()); int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first());
if (idx != -1) if (idx != -1)
m_widget->materialBrowserModel()->selectMaterial(idx); m_widget->materialBrowserModel()->selectMaterial(idx);
} else if (identifier == "refresh_material_browser") {
QTimer::singleShot(0, this, [this]() {
refreshModel(true);
});
} }
} }

View File

@@ -62,6 +62,7 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QShortcut> #include <QShortcut>
#include <QTimer> #include <QTimer>
#include <QColorDialog>
namespace QmlDesigner { 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() void MaterialEditorView::setupQmlBackend()
{ {
QUrl qmlPaneUrl; QUrl qmlPaneUrl;
@@ -472,17 +557,24 @@ void MaterialEditorView::setupQmlBackend()
currentQmlBackend->setSource(qmlPaneUrl); 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))); 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 { } else {
currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this); currentQmlBackend->setup(m_selectedMaterial, currentStateName, qmlSpecificsUrl, this);
} }
currentQmlBackend->widget()->installEventFilter(this);
currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);
m_stackedWidget->setCurrentWidget(currentQmlBackend->widget()); m_stackedWidget->setCurrentWidget(currentQmlBackend->widget());
m_qmlBackEnd = currentQmlBackend; m_qmlBackEnd = currentQmlBackend;
initPreviewData();
} }
void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
@@ -528,6 +620,29 @@ bool MaterialEditorView::noValidSelection() const
return !QmlObjectNode::isValidQmlObjectNode(m_selectedMaterial); 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) void MaterialEditorView::modelAttached(Model *model)
{ {
AbstractView::modelAttached(model); AbstractView::modelAttached(model);
@@ -844,6 +959,15 @@ void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const Prop
m_locked = false; 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() void MaterialEditorView::reloadQml()
{ {
m_qmlBackendHash.clear(); m_qmlBackendHash.clear();

View File

@@ -27,12 +27,14 @@
#include <abstractview.h> #include <abstractview.h>
#include <QHash> #include <QHash>
#include <QPointer>
#include <QTimer> #include <QTimer>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QShortcut; class QShortcut;
class QStackedWidget; class QStackedWidget;
class QTimer; class QTimer;
class QColorDialog;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace QmlDesigner { namespace QmlDesigner {
@@ -87,10 +89,13 @@ public:
public slots: public slots:
void handleToolBarAction(int action); void handleToolBarAction(int action);
void handlePreviewEnvChanged(const QString &envAndValue);
void handlePreviewModelChanged(const QString &modelStr);
protected: protected:
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value); void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
bool eventFilter(QObject *obj, QEvent *event) override;
private: private:
static QString materialEditorResourcesPath(); static QString materialEditorResourcesPath();
@@ -113,6 +118,8 @@ private:
bool noValidSelection() const; bool noValidSelection() const;
void initPreviewData();
ModelNode m_selectedMaterial; ModelNode m_selectedMaterial;
QTimer m_ensureMatLibTimer; QTimer m_ensureMatLibTimer;
QShortcut *m_updateShortcut = nullptr; QShortcut *m_updateShortcut = nullptr;
@@ -124,6 +131,8 @@ private:
bool m_locked = false; bool m_locked = false;
bool m_setupCompleted = false; bool m_setupCompleted = false;
bool m_hasQuick3DImport = false; bool m_hasQuick3DImport = false;
QPointer<QColorDialog> m_colorDialog;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -660,7 +660,8 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
const QVariant &value) const QVariant &value)
{ {
QTC_ASSERT(m_nodeInstanceServer, return); 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) if (((node.isRootNode() && (name == "width" || name == "height")) || forceAuxChange)
|| name.endsWith(PropertyName("@NodeInstance"))) { || name.endsWith(PropertyName("@NodeInstance"))) {
if (hasInstanceForModelNode(node)) { if (hasInstanceForModelNode(node)) {