forked from qt-creator/qt-creator
QmlDesigner: Unify the selection for nodes, materials, and textures
* Multiple nodes can be selected in MaterialBrowser * If a node is selected in MaterialBrowser, it's also selected in the document model. * If multiple materials/textures are selected from the outside of the MaterialBrowser, they are marked as selected in Material Browser * Right-clicking on a material/texture does not select it. The reason is that the user should be able to apply a texture to a material/model * The thick border of the focusMaterialSection is removed * A dashed-border is added to illustrate the right-clicked item * Selected items are exposed as roles for both models (material and texture). * The item found in the search is exposed as MatchedSearch role * `selectedMaterialIsComponent` is removed, and instead, a role is added to the MaterialBrowserModel Task-number: QDS-14623 Change-Id: Id0a3bd76ae795f276c36483bcc52df487070f8e4 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (C) 2025 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.Shapes
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
Shape {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool selected: false
|
||||||
|
property bool rightClicked: false
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
strokeWidth: root.rightClicked ? 2 : 0
|
||||||
|
fillColor: "transparent"
|
||||||
|
strokeColor: StudioTheme.Values.themeInteractionHover
|
||||||
|
strokeStyle: ShapePath.DashLine
|
||||||
|
dashPattern: [ 2, 4 ]
|
||||||
|
PathRectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapePath {
|
||||||
|
strokeWidth: root.selected ? 1 : 0
|
||||||
|
strokeColor: StudioTheme.Values.themeControlOutlineInteraction
|
||||||
|
fillColor: "transparent"
|
||||||
|
strokeStyle: ShapePath.PathLinear
|
||||||
|
PathRectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,9 @@ Item {
|
|||||||
readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary
|
readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary
|
||||||
&& materialBrowserModel.hasQuick3DImport
|
&& materialBrowserModel.hasQuick3DImport
|
||||||
|
|
||||||
property var currMaterialItem: null
|
property MaterialItem currMaterialItem: null
|
||||||
|
property TextureItem currTextureItem: null
|
||||||
|
|
||||||
property var rootView: MaterialBrowserBackend.rootView
|
property var rootView: MaterialBrowserBackend.rootView
|
||||||
property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
|
property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
|
||||||
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
|
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
|
||||||
@@ -64,7 +66,7 @@ Item {
|
|||||||
|
|
||||||
// Called from C++ to refresh a preview material after it changes
|
// Called from C++ to refresh a preview material after it changes
|
||||||
function refreshPreview(idx) {
|
function refreshPreview(idx) {
|
||||||
var item = materialRepeater.itemAt(idx);
|
var item = materialRepeater.itemAt(idx)
|
||||||
if (item)
|
if (item)
|
||||||
item.refreshPreview()
|
item.refreshPreview()
|
||||||
}
|
}
|
||||||
@@ -135,15 +137,18 @@ Item {
|
|||||||
let rowIdx = -1
|
let rowIdx = -1
|
||||||
let matSecFocused = rootView.materialSectionFocused && materialsSection.expanded
|
let matSecFocused = rootView.materialSectionFocused && materialsSection.expanded
|
||||||
let texSecFocused = !rootView.materialSectionFocused && texturesSection.expanded
|
let texSecFocused = !rootView.materialSectionFocused && texturesSection.expanded
|
||||||
|
let selectedMaterialIndex = root.currMaterialItem ? root.currMaterialItem.itemIndex : -1
|
||||||
|
let selectedTextureIndex = root.currTextureItem ? root.currTextureItem.itemIndex : -1
|
||||||
|
|
||||||
if (delta < 0) {
|
if (delta < 0) {
|
||||||
if (matSecFocused) {
|
if (matSecFocused) {
|
||||||
targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
|
targetIdx = root.nextVisibleItem(selectedMaterialIndex,
|
||||||
delta, materialBrowserModel)
|
delta, materialBrowserModel)
|
||||||
|
|
||||||
if (targetIdx >= 0)
|
if (targetIdx >= 0)
|
||||||
materialBrowserModel.selectMaterial(targetIdx)
|
materialBrowserModel.selectMaterial(targetIdx)
|
||||||
} else if (texSecFocused) {
|
} else if (texSecFocused) {
|
||||||
targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
targetIdx = root.nextVisibleItem(selectedTextureIndex,
|
||||||
delta, materialBrowserTexturesModel)
|
delta, materialBrowserTexturesModel)
|
||||||
if (targetIdx >= 0) {
|
if (targetIdx >= 0) {
|
||||||
materialBrowserTexturesModel.selectTexture(targetIdx)
|
materialBrowserTexturesModel.selectTexture(targetIdx)
|
||||||
@@ -152,7 +157,7 @@ Item {
|
|||||||
if (targetIdx >= 0) {
|
if (targetIdx >= 0) {
|
||||||
if (delta !== -1) {
|
if (delta !== -1) {
|
||||||
// Try to match column when switching between materials/textures
|
// Try to match column when switching between materials/textures
|
||||||
origRowIdx = root.rowIndexOfItem(materialBrowserTexturesModel.selectedIndex,
|
origRowIdx = root.rowIndexOfItem(selectedTextureIndex,
|
||||||
-delta, materialBrowserTexturesModel)
|
-delta, materialBrowserTexturesModel)
|
||||||
if (root.visibleItemCount(materialBrowserModel) > origRowIdx) {
|
if (root.visibleItemCount(materialBrowserModel) > origRowIdx) {
|
||||||
rowIdx = root.rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
|
rowIdx = root.rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
|
||||||
@@ -179,7 +184,7 @@ Item {
|
|||||||
}
|
}
|
||||||
} else if (delta > 0) {
|
} else if (delta > 0) {
|
||||||
if (matSecFocused) {
|
if (matSecFocused) {
|
||||||
targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
|
targetIdx = root.nextVisibleItem(selectedMaterialIndex,
|
||||||
delta, materialBrowserModel)
|
delta, materialBrowserModel)
|
||||||
if (targetIdx >= 0) {
|
if (targetIdx >= 0) {
|
||||||
materialBrowserModel.selectMaterial(targetIdx)
|
materialBrowserModel.selectMaterial(targetIdx)
|
||||||
@@ -188,7 +193,7 @@ Item {
|
|||||||
if (targetIdx >= 0) {
|
if (targetIdx >= 0) {
|
||||||
if (delta !== 1) {
|
if (delta !== 1) {
|
||||||
// Try to match column when switching between materials/textures
|
// Try to match column when switching between materials/textures
|
||||||
origRowIdx = root.rowIndexOfItem(materialBrowserModel.selectedIndex,
|
origRowIdx = root.rowIndexOfItem(selectedMaterialIndex,
|
||||||
delta, materialBrowserModel)
|
delta, materialBrowserModel)
|
||||||
if (root.visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
|
if (root.visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
|
||||||
if (origRowIdx > 0) {
|
if (origRowIdx > 0) {
|
||||||
@@ -207,7 +212,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (texSecFocused) {
|
} else if (texSecFocused) {
|
||||||
targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
|
targetIdx = root.nextVisibleItem(selectedTextureIndex,
|
||||||
delta, materialBrowserTexturesModel)
|
delta, materialBrowserTexturesModel)
|
||||||
if (targetIdx >= 0)
|
if (targetIdx >= 0)
|
||||||
materialBrowserTexturesModel.selectTexture(targetIdx)
|
materialBrowserTexturesModel.selectTexture(targetIdx)
|
||||||
@@ -300,14 +305,13 @@ Item {
|
|||||||
|
|
||||||
function ensureSelectedVisible() {
|
function ensureSelectedVisible() {
|
||||||
if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem
|
if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem
|
||||||
&& materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) {
|
&& root.currMaterialItem.matchedSearch) {
|
||||||
return root.ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
return root.ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
||||||
root.currMaterialItem.height)
|
root.currMaterialItem.height)
|
||||||
} else if (!rootView.materialSectionFocused && texturesSection.expanded) {
|
} else if (!rootView.materialSectionFocused && texturesSection.expanded && root.currTextureItem
|
||||||
let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex)
|
&& root.currTextureItem.matchedSearch) {
|
||||||
if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex))
|
return root.ensureVisible(root.currTextureItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
||||||
return root.ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y,
|
root.currTextureItem.height)
|
||||||
currItem.height)
|
|
||||||
} else {
|
} else {
|
||||||
return root.ensureVisible(0, 90)
|
return root.ensureVisible(0, 90)
|
||||||
}
|
}
|
||||||
@@ -341,16 +345,6 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: materialBrowserModel
|
target: materialBrowserModel
|
||||||
|
|
||||||
function onSelectedIndexChanged() {
|
|
||||||
// commit rename upon changing selection
|
|
||||||
if (root.currMaterialItem)
|
|
||||||
root.currMaterialItem.forceFinishEditing();
|
|
||||||
|
|
||||||
root.currMaterialItem = materialRepeater.itemAt(materialBrowserModel.selectedIndex);
|
|
||||||
|
|
||||||
ensureTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIsEmptyChanged() {
|
function onIsEmptyChanged() {
|
||||||
ensureTimer.start()
|
ensureTimer.start()
|
||||||
}
|
}
|
||||||
@@ -359,10 +353,6 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: materialBrowserTexturesModel
|
target: materialBrowserTexturesModel
|
||||||
|
|
||||||
function onSelectedIndexChanged() {
|
|
||||||
ensureTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIsEmptyChanged() {
|
function onIsEmptyChanged() {
|
||||||
ensureTimer.start()
|
ensureTimer.start()
|
||||||
}
|
}
|
||||||
@@ -706,10 +696,24 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delegate: MaterialItem {
|
delegate: MaterialItem {
|
||||||
|
id: matItem
|
||||||
|
|
||||||
width: root.cellWidth
|
width: root.cellWidth
|
||||||
height: root.cellHeight
|
height: root.cellHeight
|
||||||
|
rightClicked: ctxMenu.targetItem === this
|
||||||
|
|
||||||
onShowContextMenu: ctxMenu.popupMenu(this, model)
|
onShowContextMenu: ctxMenu.popupMenu(this, model)
|
||||||
|
|
||||||
|
onSelectedChanged: {
|
||||||
|
matItem.forceFinishEditing()
|
||||||
|
|
||||||
|
if (matItem.selected) {
|
||||||
|
root.currMaterialItem = this
|
||||||
|
ensureTimer.start()
|
||||||
|
} else if (root.currMaterialItem === this) {
|
||||||
|
root.currMaterialItem = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCountChanged: root.responsiveResize(root.width, root.height)
|
onCountChanged: root.responsiveResize(root.width, root.height)
|
||||||
@@ -717,7 +721,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("No match found.");
|
text: qsTr("No match found.")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
@@ -801,10 +805,24 @@ Item {
|
|||||||
|
|
||||||
model: materialBrowserTexturesModel
|
model: materialBrowserTexturesModel
|
||||||
delegate: TextureItem {
|
delegate: TextureItem {
|
||||||
|
id: texItem
|
||||||
|
|
||||||
width: root.cellWidth
|
width: root.cellWidth
|
||||||
height: root.cellHeight
|
height: root.cellHeight
|
||||||
|
rightClicked: ctxMenuTextures.textureIndex === index
|
||||||
|
|
||||||
onShowContextMenu: ctxMenuTextures.popupMenu(model)
|
onShowContextMenu: ctxMenuTextures.popupMenu(model)
|
||||||
|
|
||||||
|
onSelectedChanged: {
|
||||||
|
texItem.forceFinishEditing()
|
||||||
|
|
||||||
|
if (texItem.selected) {
|
||||||
|
root.currTextureItem = this
|
||||||
|
ensureTimer.start()
|
||||||
|
} else if (root.currTextureItem === this) {
|
||||||
|
root.currTextureItem = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCountChanged: root.responsiveResize(root.width, root.height)
|
onCountChanged: root.responsiveResize(root.width, root.height)
|
||||||
@@ -812,7 +830,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("No match found.");
|
text: qsTr("No match found.")
|
||||||
color: StudioTheme.Values.themeTextColor
|
color: StudioTheme.Values.themeTextColor
|
||||||
font.pixelSize: StudioTheme.Values.baseFontSize
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
|
@@ -12,6 +12,7 @@ StudioControls.Menu {
|
|||||||
|
|
||||||
property var targetMaterial: null
|
property var targetMaterial: null
|
||||||
property var targetItem: null
|
property var targetItem: null
|
||||||
|
property int targetIndex: -1
|
||||||
property int copiedMaterialInternalId: -1
|
property int copiedMaterialInternalId: -1
|
||||||
property var matSectionsModel: []
|
property var matSectionsModel: []
|
||||||
property bool restoreFocusOnClose: true
|
property bool restoreFocusOnClose: true
|
||||||
@@ -22,12 +23,19 @@ StudioControls.Menu {
|
|||||||
{
|
{
|
||||||
this.targetItem = targetItem
|
this.targetItem = targetItem
|
||||||
this.targetMaterial = targetMaterial
|
this.targetMaterial = targetMaterial
|
||||||
|
this.targetIndex = targetMaterial ? targetMaterial.index : -1
|
||||||
restoreFocusOnClose = true
|
restoreFocusOnClose = true
|
||||||
popup()
|
popup()
|
||||||
}
|
}
|
||||||
|
|
||||||
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
this.targetItem = null
|
||||||
|
this.targetMaterial = null
|
||||||
|
this.targetIndex = -1
|
||||||
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Apply to selected (replace)")
|
text: qsTr("Apply to selected (replace)")
|
||||||
enabled: root.targetMaterial && materialBrowserModel.hasModelSelection
|
enabled: root.targetMaterial && materialBrowserModel.hasModelSelection
|
||||||
@@ -50,26 +58,26 @@ StudioControls.Menu {
|
|||||||
|
|
||||||
onAboutToShow: {
|
onAboutToShow: {
|
||||||
if (root.targetMaterial.hasDynamicProperties)
|
if (root.targetMaterial.hasDynamicProperties)
|
||||||
root.matSectionsModel = ["All", "Custom"];
|
root.matSectionsModel = ["All", "Custom"]
|
||||||
else
|
else
|
||||||
root.matSectionsModel = ["All"];
|
root.matSectionsModel = ["All"]
|
||||||
|
|
||||||
switch (root.targetMaterial.materialType) {
|
switch (root.targetMaterial.materialType) {
|
||||||
case "DefaultMaterial":
|
case "DefaultMaterial":
|
||||||
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.defaultMaterialSections);
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.defaultMaterialSections)
|
||||||
break;
|
break
|
||||||
|
|
||||||
case "PrincipledMaterial":
|
case "PrincipledMaterial":
|
||||||
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.principledMaterialSections);
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.principledMaterialSections)
|
||||||
break;
|
break
|
||||||
|
|
||||||
case "SpecularGlossyMaterial":
|
case "SpecularGlossyMaterial":
|
||||||
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.specularGlossyMaterialSections);
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.specularGlossyMaterialSections)
|
||||||
break;
|
break
|
||||||
|
|
||||||
case "CustomMaterial":
|
case "CustomMaterial":
|
||||||
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.customMaterialSections);
|
root.matSectionsModel = root.matSectionsModel.concat(materialBrowserModel.customMaterialSections)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +89,7 @@ StudioControls.Menu {
|
|||||||
enabled: root.targetMaterial
|
enabled: root.targetMaterial
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.copiedMaterialInternalId = root.targetMaterial.materialInternalId
|
root.copiedMaterialInternalId = root.targetMaterial.materialInternalId
|
||||||
materialBrowserModel.copyMaterialProperties(materialBrowserModel.selectedIndex, modelData)
|
materialBrowserModel.copyMaterialProperties(root.targetIndex, modelData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +101,7 @@ StudioControls.Menu {
|
|||||||
&& root.copiedMaterialInternalId !== root.targetMaterial.materialInternalId
|
&& root.copiedMaterialInternalId !== root.targetMaterial.materialInternalId
|
||||||
&& root.targetMaterial.materialType === materialBrowserModel.copiedMaterialType
|
&& root.targetMaterial.materialType === materialBrowserModel.copiedMaterialType
|
||||||
&& materialBrowserModel.isCopiedMaterialValid()
|
&& materialBrowserModel.isCopiedMaterialValid()
|
||||||
onTriggered: materialBrowserModel.pasteMaterialProperties(materialBrowserModel.selectedIndex)
|
onTriggered: materialBrowserModel.pasteMaterialProperties(root.targetIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {}
|
StudioControls.MenuSeparator {}
|
||||||
@@ -101,7 +109,7 @@ StudioControls.Menu {
|
|||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Duplicate")
|
text: qsTr("Duplicate")
|
||||||
enabled: root.targetMaterial
|
enabled: root.targetMaterial
|
||||||
onTriggered: materialBrowserModel.duplicateMaterial(materialBrowserModel.selectedIndex)
|
onTriggered: materialBrowserModel.duplicateMaterial(root.targetIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
@@ -117,7 +125,7 @@ StudioControls.Menu {
|
|||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
enabled: root.targetMaterial
|
enabled: root.targetMaterial
|
||||||
|
|
||||||
onTriggered: materialBrowserModel.deleteMaterial(materialBrowserModel.selectedIndex)
|
onTriggered: materialBrowserModel.deleteMaterial(root.targetIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {}
|
StudioControls.MenuSeparator {}
|
||||||
@@ -131,7 +139,7 @@ StudioControls.Menu {
|
|||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Add to Content Library")
|
text: qsTr("Add to Content Library")
|
||||||
|
|
||||||
onTriggered: MaterialBrowserBackend.rootView.addMaterialToContentLibrary()
|
onTriggered: MaterialBrowserBackend.rootView.addMaterialToContentLibrary(root.targetMaterial)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
@@ -142,8 +150,8 @@ StudioControls.Menu {
|
|||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Export Material")
|
text: qsTr("Export Material")
|
||||||
enabled: !materialBrowserModel.selectedMaterialIsComponent // TODO: support component materials
|
enabled: root.targetMaterial && !root.targetMaterial.materialIsComponent // TODO: support component materials
|
||||||
|
|
||||||
onTriggered: MaterialBrowserBackend.rootView.exportMaterial()
|
onTriggered: MaterialBrowserBackend.rootView.exportMaterial(root.targetIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ TextInput {
|
|||||||
validator: RegularExpressionValidator { regularExpression: /^(\w+\s)*\w+$/ }
|
validator: RegularExpressionValidator { regularExpression: /^(\w+\s)*\w+$/ }
|
||||||
|
|
||||||
signal renamed(string newName)
|
signal renamed(string newName)
|
||||||
signal clicked()
|
signal clicked(var mouseEvent)
|
||||||
|
|
||||||
function startRename()
|
function startRename()
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ TextInput {
|
|||||||
function commitRename()
|
function commitRename()
|
||||||
{
|
{
|
||||||
if (root.readOnly)
|
if (root.readOnly)
|
||||||
return;
|
return
|
||||||
|
|
||||||
root.renamed(root.text)
|
root.renamed(root.text)
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ TextInput {
|
|||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: root.clicked()
|
onClicked: (mouseEvent) => root.clicked(mouseEvent)
|
||||||
onDoubleClicked: root.startRename()
|
onDoubleClicked: root.startRename()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,11 @@ import MaterialBrowserBackend
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property bool selected: materialSelected?? false
|
||||||
|
readonly property bool matchedSearch: materialMatchedSearch?? false
|
||||||
|
readonly property int itemIndex: index
|
||||||
|
property bool rightClicked: false
|
||||||
|
|
||||||
signal showContextMenu()
|
signal showContextMenu()
|
||||||
|
|
||||||
function refreshPreview() {
|
function refreshPreview() {
|
||||||
@@ -25,7 +30,7 @@ Item {
|
|||||||
matName.startRename()
|
matName.startRename()
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: materialVisible
|
visible: matchedSearch
|
||||||
|
|
||||||
DropArea {
|
DropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -55,17 +60,20 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
function handleClick(mouse) {
|
||||||
MaterialBrowserBackend.materialBrowserModel.selectMaterial(index)
|
|
||||||
MaterialBrowserBackend.rootView.focusMaterialSection(true)
|
MaterialBrowserBackend.rootView.focusMaterialSection(true)
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton)
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
let appendMat = mouse.modifiers & Qt.ControlModifier
|
||||||
MaterialBrowserBackend.rootView.startDragMaterial(index, mapToGlobal(mouse.x, mouse.y))
|
MaterialBrowserBackend.rootView.startDragMaterial(index, mapToGlobal(mouse.x, mouse.y))
|
||||||
else if (mouse.button === Qt.RightButton)
|
MaterialBrowserBackend.materialBrowserModel.selectMaterial(index, appendMat)
|
||||||
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
root.showContextMenu()
|
root.showContextMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDoubleClicked: MaterialBrowserBackend.materialBrowserModel.openMaterialEditor();
|
onPressed: (mouse) => handleClick(mouse)
|
||||||
|
onDoubleClicked: MaterialBrowserBackend.materialBrowserModel.openMaterialEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -95,24 +103,16 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
onRenamed: (newName) => {
|
onRenamed: (newName) => {
|
||||||
MaterialBrowserBackend.materialBrowserModel.renameMaterial(index, newName);
|
MaterialBrowserBackend.materialBrowserModel.renameMaterial(index, newName)
|
||||||
mouseArea.forceActiveFocus()
|
mouseArea.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: (mouse) => mouseArea.handleClick(mouse)
|
||||||
MaterialBrowserBackend.materialBrowserModel.selectMaterial(index)
|
|
||||||
MaterialBrowserBackend.rootView.focusMaterialSection(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
ItemBorder {
|
||||||
id: marker
|
selected: root.selected
|
||||||
anchors.fill: parent
|
rightClicked: root.rightClicked
|
||||||
border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
|
||||||
border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
|
|
||||||
? StudioTheme.Values.themeControlOutlineInteraction
|
|
||||||
: "transparent"
|
|
||||||
color: "transparent"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,21 +11,28 @@ StudioControls.Menu {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int textureInternalId: -1
|
property int textureInternalId: -1
|
||||||
|
property int textureIndex: -1
|
||||||
|
|
||||||
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
|
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
|
||||||
|
|
||||||
function popupMenu(targetTexture = null)
|
function popupMenu(targetTexture = null)
|
||||||
{
|
{
|
||||||
root.textureInternalId = targetTexture ? targetTexture.textureInternalId : -1
|
root.textureInternalId = targetTexture ? targetTexture.textureInternalId : -1
|
||||||
|
root.textureIndex = targetTexture ? targetTexture.index : -1
|
||||||
|
|
||||||
materialBrowserTexturesModel.updateSceneEnvState()
|
materialBrowserTexturesModel.updateSceneEnvState()
|
||||||
materialBrowserTexturesModel.updateModelSelectionState()
|
materialBrowserTexturesModel.updateSelectionState()
|
||||||
|
|
||||||
popup()
|
popup()
|
||||||
}
|
}
|
||||||
|
|
||||||
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
root.textureIndex = -1
|
||||||
|
root.textureInternalId = -1
|
||||||
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Apply to selected model")
|
text: qsTr("Apply to selected model")
|
||||||
enabled: root.textureInternalId >= 0 && materialBrowserTexturesModel.hasSingleModelSelection
|
enabled: root.textureInternalId >= 0 && materialBrowserTexturesModel.hasSingleModelSelection
|
||||||
@@ -33,8 +40,8 @@ StudioControls.Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Apply to selected material")
|
text: qsTr("Apply to selected material(s)")
|
||||||
enabled: root.textureInternalId >= 0 && MaterialBrowserBackend.materialBrowserModel.selectedIndex >= 0
|
enabled: root.textureInternalId >= 0 && materialBrowserTexturesModel.onlyMaterialsSelected
|
||||||
onTriggered: materialBrowserTexturesModel.applyToSelectedMaterial(root.textureInternalId)
|
onTriggered: materialBrowserTexturesModel.applyToSelectedMaterial(root.textureInternalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +56,13 @@ StudioControls.Menu {
|
|||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Duplicate")
|
text: qsTr("Duplicate")
|
||||||
enabled: root.textureInternalId >= 0
|
enabled: root.textureInternalId >= 0
|
||||||
onTriggered: materialBrowserTexturesModel.duplicateTexture(materialBrowserTexturesModel.selectedIndex)
|
onTriggered: materialBrowserTexturesModel.duplicateTexture(root.textureIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
enabled: root.textureInternalId >= 0
|
enabled: root.textureInternalId >= 0
|
||||||
onTriggered: materialBrowserTexturesModel.deleteTexture(materialBrowserTexturesModel.selectedIndex)
|
onTriggered: materialBrowserTexturesModel.deleteTexture(root.textureIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {}
|
StudioControls.MenuSeparator {}
|
||||||
|
@@ -11,7 +11,12 @@ import MaterialBrowserBackend
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
visible: textureVisible
|
readonly property bool selected: textureSelected?? false
|
||||||
|
readonly property bool matchedSearch: textureMatchedSearch?? false
|
||||||
|
readonly property int itemIndex: index
|
||||||
|
property bool rightClicked: false
|
||||||
|
|
||||||
|
visible: matchedSearch
|
||||||
|
|
||||||
signal showContextMenu()
|
signal showContextMenu()
|
||||||
|
|
||||||
@@ -26,17 +31,20 @@ Item {
|
|||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
function handleClick(mouse) {
|
||||||
MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index)
|
|
||||||
MaterialBrowserBackend.rootView.focusMaterialSection(false)
|
MaterialBrowserBackend.rootView.focusMaterialSection(false)
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton)
|
if (mouse.button === Qt.LeftButton) {
|
||||||
|
let appendTexture = mouse.modifiers & Qt.ControlModifier
|
||||||
|
MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index, appendTexture)
|
||||||
MaterialBrowserBackend.rootView.startDragTexture(index, mapToGlobal(mouse.x, mouse.y))
|
MaterialBrowserBackend.rootView.startDragTexture(index, mapToGlobal(mouse.x, mouse.y))
|
||||||
else if (mouse.button === Qt.RightButton)
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
root.showContextMenu()
|
root.showContextMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDoubleClicked: MaterialBrowserBackend.materialBrowserTexturesModel.openTextureEditor();
|
onPressed: (mouse) => handleClick(mouse)
|
||||||
|
onDoubleClicked: MaterialBrowserBackend.materialBrowserTexturesModel.openTextureEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip {
|
ToolTip {
|
||||||
@@ -83,26 +91,16 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
onRenamed: (newName) => {
|
onRenamed: (newName) => {
|
||||||
MaterialBrowserBackend.materialBrowserTexturesModel.setTextureName(index, newName);
|
MaterialBrowserBackend.materialBrowserTexturesModel.setTextureName(index, newName)
|
||||||
mouseArea.forceActiveFocus()
|
mouseArea.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: (mouse) => mouseArea.handleClick(mouse)
|
||||||
MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index)
|
|
||||||
MaterialBrowserBackend.rootView.focusMaterialSection(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
ItemBorder {
|
||||||
id: marker
|
selected: root.selected
|
||||||
anchors.fill: parent
|
rightClicked: root.rightClicked
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
|
||||||
? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
|
|
||||||
border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
|
|
||||||
? StudioTheme.Values.themeControlOutlineInteraction
|
|
||||||
: "transparent"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -234,6 +234,26 @@ QList<ModelNode> getSelectedModels(AbstractView *view)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ModelNode> getSelectedTextures(AbstractView *view)
|
||||||
|
{
|
||||||
|
if (!view || !view->model())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Utils::filtered(view->selectedModelNodes(), [](const ModelNode &node) {
|
||||||
|
return node.metaInfo().isQtQuick3DTexture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ModelNode> getSelectedMaterials(AbstractView *view)
|
||||||
|
{
|
||||||
|
if (!view || !view->model())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Utils::filtered(view->selectedModelNodes(), [](const ModelNode &node) {
|
||||||
|
return node.metaInfo().isQtQuick3DMaterial();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void applyMaterialToModels(AbstractView *view, const ModelNode &material,
|
void applyMaterialToModels(AbstractView *view, const ModelNode &material,
|
||||||
const QList<ModelNode> &models, bool add)
|
const QList<ModelNode> &models, bool add)
|
||||||
{
|
{
|
||||||
|
@@ -43,6 +43,8 @@ ModelNode selectedTexture(AbstractView *view);
|
|||||||
ModelNode resolveSceneEnv(AbstractView *view, int sceneId);
|
ModelNode resolveSceneEnv(AbstractView *view, int sceneId);
|
||||||
|
|
||||||
QList<ModelNode> getSelectedModels(AbstractView *view);
|
QList<ModelNode> getSelectedModels(AbstractView *view);
|
||||||
|
QList<ModelNode> getSelectedTextures(AbstractView *view);
|
||||||
|
QList<ModelNode> getSelectedMaterials(AbstractView *view);
|
||||||
void applyMaterialToModels(AbstractView *view, const ModelNode &material,
|
void applyMaterialToModels(AbstractView *view, const ModelNode &material,
|
||||||
const QList<ModelNode> &models, bool add = false);
|
const QList<ModelNode> &models, bool add = false);
|
||||||
|
|
||||||
|
@@ -33,32 +33,32 @@ int MaterialBrowserModel::rowCount(const QModelIndex &) const
|
|||||||
|
|
||||||
QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
|
QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(index.isValid() && index.row() < m_materialList.size(), return {});
|
QTC_ASSERT(index.isValid(), return {});
|
||||||
QTC_ASSERT(roleNames().contains(role), return {});
|
|
||||||
|
|
||||||
QByteArray roleName = roleNames().value(role);
|
switch (role) {
|
||||||
if (roleName == "materialName") {
|
case Roles::NameRole: {
|
||||||
QVariant objName = m_materialList.at(index.row()).variantProperty("objectName").value();
|
QVariant objName = m_materialList.at(index.row()).variantProperty("objectName").value();
|
||||||
return objName.isValid() ? objName : "";
|
return objName.isValid() ? objName : "";
|
||||||
}
|
} break;
|
||||||
|
case Roles::InternalIdRole:
|
||||||
if (roleName == "materialInternalId")
|
|
||||||
return m_materialList.at(index.row()).internalId();
|
return m_materialList.at(index.row()).internalId();
|
||||||
|
case Roles::MatchedSearchRole:
|
||||||
if (roleName == "materialVisible")
|
|
||||||
return isVisible(index.row());
|
return isVisible(index.row());
|
||||||
|
case Roles::SelectedRole:
|
||||||
if (roleName == "materialType") {
|
return m_materialList.at(index.row()).isSelected();
|
||||||
|
case Roles::IsComponentRole:
|
||||||
|
return m_materialList.at(index.row()).isComponent();
|
||||||
|
case Roles::TypeRole: {
|
||||||
QString matType = QString::fromLatin1(m_materialList.at(index.row()).type());
|
QString matType = QString::fromLatin1(m_materialList.at(index.row()).type());
|
||||||
if (matType.startsWith("QtQuick3D."))
|
if (matType.startsWith("QtQuick3D."))
|
||||||
matType.remove("QtQuick3D.");
|
matType.remove("QtQuick3D.");
|
||||||
return matType;
|
return matType;
|
||||||
}
|
} break;
|
||||||
|
case Roles::HasDynamicPropertiesRole:
|
||||||
if (roleName == "hasDynamicProperties")
|
|
||||||
return !m_materialList.at(index.row()).dynamicProperties().isEmpty();
|
return !m_materialList.at(index.row()).dynamicProperties().isEmpty();
|
||||||
|
default:
|
||||||
return {};
|
return {};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaterialBrowserModel::isVisible(int idx) const
|
bool MaterialBrowserModel::isVisible(int idx) const
|
||||||
@@ -137,12 +137,14 @@ void MaterialBrowserModel::unloadPropertyGroups()
|
|||||||
|
|
||||||
QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
|
QHash<int, QByteArray> MaterialBrowserModel::roleNames() const
|
||||||
{
|
{
|
||||||
static const QHash<int, QByteArray> roles {
|
static const QHash<int, QByteArray> roles{
|
||||||
{Qt::UserRole + 1, "materialName"},
|
{Roles::NameRole, "materialName"},
|
||||||
{Qt::UserRole + 2, "materialInternalId"},
|
{Roles::InternalIdRole, "materialInternalId"},
|
||||||
{Qt::UserRole + 3, "materialVisible"},
|
{Roles::MatchedSearchRole, "materialMatchedSearch"},
|
||||||
{Qt::UserRole + 4, "materialType"},
|
{Roles::SelectedRole, "materialSelected"},
|
||||||
{Qt::UserRole + 5, "hasDynamicProperties"}
|
{Roles::IsComponentRole, "materialIsComponent"},
|
||||||
|
{Roles::TypeRole, "materialType"},
|
||||||
|
{Roles::HasDynamicPropertiesRole, "hasDynamicProperties"},
|
||||||
};
|
};
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
@@ -236,26 +238,13 @@ void MaterialBrowserModel::setSearchText(const QString &searchText)
|
|||||||
|
|
||||||
void MaterialBrowserModel::refreshSearch()
|
void MaterialBrowserModel::refreshSearch()
|
||||||
{
|
{
|
||||||
bool isEmpty = false;
|
bool isEmpty = true;
|
||||||
|
|
||||||
// if selected material goes invisible, select nearest material
|
for (int i = 0; i < m_materialList.size(); ++i) {
|
||||||
if (!isVisible(m_selectedIndex)) {
|
if (isVisible(i)) {
|
||||||
int inc = 1;
|
isEmpty = false;
|
||||||
int incCap = m_materialList.size();
|
break;
|
||||||
while (!isEmpty && inc < incCap) {
|
|
||||||
if (isVisible(m_selectedIndex - inc)) {
|
|
||||||
selectMaterial(m_selectedIndex - inc);
|
|
||||||
break;
|
|
||||||
} else if (isVisible(m_selectedIndex + inc)) {
|
|
||||||
selectMaterial(m_selectedIndex + inc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++inc;
|
|
||||||
isEmpty = !isValidIndex(m_selectedIndex + inc)
|
|
||||||
&& !isValidIndex(m_selectedIndex - inc);
|
|
||||||
}
|
}
|
||||||
if (!isVisible(m_selectedIndex)) // handles the case of a single material
|
|
||||||
isEmpty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmpty != m_isEmpty) {
|
if (isEmpty != m_isEmpty) {
|
||||||
@@ -284,7 +273,6 @@ void MaterialBrowserModel::setMaterials(const QList<ModelNode> &materials, bool
|
|||||||
else
|
else
|
||||||
resetModel();
|
resetModel();
|
||||||
|
|
||||||
updateSelectedMaterial();
|
|
||||||
setHasQuick3DImport(hasQuick3DImport);
|
setHasQuick3DImport(hasQuick3DImport);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,19 +297,18 @@ void MaterialBrowserModel::removeMaterial(const ModelNode &material)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::deleteSelectedMaterial()
|
void MaterialBrowserModel::deleteSelectedMaterials()
|
||||||
{
|
{
|
||||||
deleteMaterial(m_selectedIndex);
|
m_view->executeInTransaction(__FUNCTION__, [this] {
|
||||||
}
|
QStack<int> selectedIndexes;
|
||||||
|
for (int i = 0; i < m_materialList.size(); ++i) {
|
||||||
|
if (m_materialList.at(i).isSelected())
|
||||||
|
selectedIndexes << i;
|
||||||
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::updateSelectedMaterial()
|
while (!selectedIndexes.isEmpty())
|
||||||
{
|
deleteMaterial(selectedIndexes.pop());
|
||||||
if (!m_materialList.isEmpty() && m_selectedIndex < 0) {
|
});
|
||||||
ModelNode mat = Utils3D::selectedMaterial(m_view);
|
|
||||||
m_selectedIndex = materialIndex(mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectMaterial(m_selectedIndex, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::updateMaterialName(const ModelNode &material)
|
void MaterialBrowserModel::updateMaterialName(const ModelNode &material)
|
||||||
@@ -344,36 +331,51 @@ ModelNode MaterialBrowserModel::materialAt(int idx) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelNode MaterialBrowserModel::selectedMaterial() const
|
|
||||||
{
|
|
||||||
if (isValidIndex(m_selectedIndex))
|
|
||||||
return m_materialList[m_selectedIndex];
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void MaterialBrowserModel::resetModel()
|
void MaterialBrowserModel::resetModel()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::selectMaterial(int idx, bool force)
|
void MaterialBrowserModel::notifySelectionChanges(const QList<ModelNode> &selectedNodes,
|
||||||
|
const QList<ModelNode> &deselectedNodes)
|
||||||
{
|
{
|
||||||
if (m_materialList.size() == 0) {
|
QList<int> indices;
|
||||||
m_selectedIndex = -1;
|
indices.reserve(selectedNodes.size() + deselectedNodes.size());
|
||||||
emit selectedIndexChanged(m_selectedIndex);
|
for (const ModelNode &node : selectedNodes)
|
||||||
|
indices.append(materialIndex(node));
|
||||||
|
|
||||||
|
for (const ModelNode &node : deselectedNodes)
|
||||||
|
indices.append(materialIndex(node));
|
||||||
|
|
||||||
|
using Bound = QPair<int, int>;
|
||||||
|
const QList<Bound> &bounds = MaterialBrowserView::getSortedBounds(indices);
|
||||||
|
|
||||||
|
for (const Bound &bound : bounds)
|
||||||
|
emit dataChanged(index(bound.first), index(bound.second), {Roles::SelectedRole});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserModel::updateMaterialComponent(int idx)
|
||||||
|
{
|
||||||
|
if (!isValidIndex(idx))
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
idx = std::max(0, std::min(idx, rowCount() - 1));
|
const QModelIndex &mIdx = index(idx);
|
||||||
|
emit dataChanged(mIdx, mIdx, {Roles::IsComponentRole});
|
||||||
|
}
|
||||||
|
|
||||||
if (idx != m_selectedIndex || force) {
|
void MaterialBrowserModel::selectMaterial(int idx, bool appendMat)
|
||||||
m_selectedIndex = idx;
|
{
|
||||||
emit selectedIndexChanged(idx);
|
if (!isValidIndex(idx))
|
||||||
|
return;
|
||||||
|
|
||||||
m_selectedMaterialIsComponent = selectedMaterial().isComponent();
|
ModelNode mat = m_materialList.at(idx);
|
||||||
emit selectedMaterialIsComponentChanged();
|
QTC_ASSERT(mat, return);
|
||||||
}
|
|
||||||
|
if (appendMat)
|
||||||
|
mat.view()->selectModelNode(mat);
|
||||||
|
else
|
||||||
|
mat.selectNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserModel::duplicateMaterial(int idx)
|
void MaterialBrowserModel::duplicateMaterial(int idx)
|
||||||
|
@@ -19,8 +19,6 @@ class MaterialBrowserModel : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||||
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
|
||||||
Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged)
|
|
||||||
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
|
||||||
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
|
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
|
||||||
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
|
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
|
||||||
@@ -62,18 +60,19 @@ public:
|
|||||||
QList<ModelNode> materials() const;
|
QList<ModelNode> materials() const;
|
||||||
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
|
void setMaterials(const QList<ModelNode> &materials, bool hasQuick3DImport);
|
||||||
void removeMaterial(const ModelNode &material);
|
void removeMaterial(const ModelNode &material);
|
||||||
void deleteSelectedMaterial();
|
void deleteSelectedMaterials();
|
||||||
void updateMaterialName(const ModelNode &material);
|
void updateMaterialName(const ModelNode &material);
|
||||||
void updateSelectedMaterial();
|
|
||||||
int materialIndex(const ModelNode &material) const;
|
int materialIndex(const ModelNode &material) const;
|
||||||
ModelNode materialAt(int idx) const;
|
ModelNode materialAt(int idx) const;
|
||||||
ModelNode selectedMaterial() const;
|
|
||||||
bool loadPropertyGroups(const QString &path);
|
bool loadPropertyGroups(const QString &path);
|
||||||
void unloadPropertyGroups();
|
void unloadPropertyGroups();
|
||||||
|
|
||||||
void resetModel();
|
void resetModel();
|
||||||
|
void notifySelectionChanges(const QList<ModelNode> &selectedNodes,
|
||||||
|
const QList<ModelNode> &deselectedNodes);
|
||||||
|
void updateMaterialComponent(int idx);
|
||||||
|
|
||||||
Q_INVOKABLE void selectMaterial(int idx, bool force = false);
|
Q_INVOKABLE void selectMaterial(int idx, bool appendMat = false);
|
||||||
Q_INVOKABLE void duplicateMaterial(int idx);
|
Q_INVOKABLE void duplicateMaterial(int idx);
|
||||||
Q_INVOKABLE void copyMaterialProperties(int idx, const QString §ion);
|
Q_INVOKABLE void copyMaterialProperties(int idx, const QString §ion);
|
||||||
Q_INVOKABLE void pasteMaterialProperties(int idx);
|
Q_INVOKABLE void pasteMaterialProperties(int idx);
|
||||||
@@ -101,7 +100,6 @@ signals:
|
|||||||
void hasMaterialLibraryChanged();
|
void hasMaterialLibraryChanged();
|
||||||
void copiedMaterialTypeChanged();
|
void copiedMaterialTypeChanged();
|
||||||
void materialSectionsChanged();
|
void materialSectionsChanged();
|
||||||
void selectedIndexChanged(int idx);
|
|
||||||
void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName);
|
void renameMaterialTriggered(const QmlDesigner::ModelNode &material, const QString &newName);
|
||||||
void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
void applyToSelectedTriggered(const QmlDesigner::ModelNode &material, bool add = false);
|
||||||
void addNewMaterialTriggered();
|
void addNewMaterialTriggered();
|
||||||
@@ -111,11 +109,20 @@ signals:
|
|||||||
const QList<QmlDesigner::MaterialBrowserModel::PropertyCopyData> &props,
|
const QList<QmlDesigner::MaterialBrowserModel::PropertyCopyData> &props,
|
||||||
bool all);
|
bool all);
|
||||||
void isQt6ProjectChanged();
|
void isQt6ProjectChanged();
|
||||||
void selectedMaterialIsComponentChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isValidIndex(int idx) const;
|
bool isValidIndex(int idx) const;
|
||||||
|
|
||||||
|
enum Roles {
|
||||||
|
NameRole = Qt::UserRole + 1,
|
||||||
|
InternalIdRole,
|
||||||
|
MatchedSearchRole,
|
||||||
|
SelectedRole,
|
||||||
|
IsComponentRole,
|
||||||
|
TypeRole,
|
||||||
|
HasDynamicPropertiesRole,
|
||||||
|
};
|
||||||
|
|
||||||
QString m_searchText;
|
QString m_searchText;
|
||||||
QList<ModelNode> m_materialList;
|
QList<ModelNode> m_materialList;
|
||||||
QStringList m_defaultMaterialSections;
|
QStringList m_defaultMaterialSections;
|
||||||
@@ -127,14 +134,12 @@ private:
|
|||||||
QHash<qint32, int> m_materialIndexHash; // internalId -> index
|
QHash<qint32, int> m_materialIndexHash; // internalId -> index
|
||||||
QJsonObject m_propertyGroupsObj;
|
QJsonObject m_propertyGroupsObj;
|
||||||
|
|
||||||
int m_selectedIndex = -1;
|
|
||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
bool m_hasQuick3DImport = false;
|
bool m_hasQuick3DImport = false;
|
||||||
bool m_hasModelSelection = false;
|
bool m_hasModelSelection = false;
|
||||||
bool m_hasMaterialLibrary = false;
|
bool m_hasMaterialLibrary = false;
|
||||||
bool m_allPropsCopied = true;
|
bool m_allPropsCopied = true;
|
||||||
bool m_isQt6Project = false;
|
bool m_isQt6Project = false;
|
||||||
bool m_selectedMaterialIsComponent = false;
|
|
||||||
QString m_copiedMaterialType;
|
QString m_copiedMaterialType;
|
||||||
|
|
||||||
QPointer<MaterialBrowserView> m_view;
|
QPointer<MaterialBrowserView> m_view;
|
||||||
|
@@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
static bool isMaterial(const ModelNode &node)
|
||||||
|
{
|
||||||
|
return node.metaInfo().isQtQuick3DMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
MaterialBrowserTexturesModel::MaterialBrowserTexturesModel(MaterialBrowserView *view, QObject *parent)
|
MaterialBrowserTexturesModel::MaterialBrowserTexturesModel(MaterialBrowserView *view, QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_view(view)
|
, m_view(view)
|
||||||
@@ -37,8 +42,10 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
|
|||||||
QTC_ASSERT(roleNames().contains(role), return {});
|
QTC_ASSERT(roleNames().contains(role), return {});
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case RoleTexVisible:
|
case RoleMatchedSearch:
|
||||||
return isVisible(index.row());
|
return isVisible(index.row());
|
||||||
|
case RoleTexSelected:
|
||||||
|
return m_textureList.at(index.row()).isSelected();
|
||||||
case RoleTexHasDynamicProps:
|
case RoleTexHasDynamicProps:
|
||||||
return !m_textureList.at(index.row()).dynamicProperties().isEmpty();
|
return !m_textureList.at(index.row()).dynamicProperties().isEmpty();
|
||||||
case RoleTexInternalId:
|
case RoleTexInternalId:
|
||||||
@@ -102,6 +109,15 @@ bool MaterialBrowserTexturesModel::isValidIndex(int idx) const
|
|||||||
return idx > -1 && idx < rowCount();
|
return idx > -1 && idx < rowCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserTexturesModel::setOnlyMaterialsSelected(bool value)
|
||||||
|
{
|
||||||
|
if (m_onlyMaterialsSelected == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_onlyMaterialsSelected = value;
|
||||||
|
emit onlyMaterialsSelectedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> MaterialBrowserTexturesModel::roleNames() const
|
QHash<int, QByteArray> MaterialBrowserTexturesModel::roleNames() const
|
||||||
{
|
{
|
||||||
static const QHash<int, QByteArray> roles{
|
static const QHash<int, QByteArray> roles{
|
||||||
@@ -110,7 +126,8 @@ QHash<int, QByteArray> MaterialBrowserTexturesModel::roleNames() const
|
|||||||
{RoleTexName, "textureName"},
|
{RoleTexName, "textureName"},
|
||||||
{RoleTexSource, "textureSource"},
|
{RoleTexSource, "textureSource"},
|
||||||
{RoleTexToolTip, "textureToolTip"},
|
{RoleTexToolTip, "textureToolTip"},
|
||||||
{RoleTexVisible, "textureVisible"},
|
{RoleMatchedSearch, "textureMatchedSearch"},
|
||||||
|
{RoleTexSelected, "textureSelected"},
|
||||||
};
|
};
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
@@ -134,26 +151,13 @@ void MaterialBrowserTexturesModel::setSearchText(const QString &searchText)
|
|||||||
|
|
||||||
void MaterialBrowserTexturesModel::refreshSearch()
|
void MaterialBrowserTexturesModel::refreshSearch()
|
||||||
{
|
{
|
||||||
bool isEmpty = false;
|
bool isEmpty = true;
|
||||||
|
|
||||||
// if selected texture goes invisible, select nearest one
|
for (int i = 0; i < m_textureList.size(); ++i) {
|
||||||
if (!isVisible(m_selectedIndex)) {
|
if (isVisible(i)) {
|
||||||
int inc = 1;
|
isEmpty = false;
|
||||||
int incCap = m_textureList.size();
|
break;
|
||||||
while (!isEmpty && inc < incCap) {
|
|
||||||
if (isVisible(m_selectedIndex - inc)) {
|
|
||||||
selectTexture(m_selectedIndex - inc);
|
|
||||||
break;
|
|
||||||
} else if (isVisible(m_selectedIndex + inc)) {
|
|
||||||
selectTexture(m_selectedIndex + inc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++inc;
|
|
||||||
isEmpty = !isValidIndex(m_selectedIndex + inc)
|
|
||||||
&& !isValidIndex(m_selectedIndex - inc);
|
|
||||||
}
|
}
|
||||||
if (!isVisible(m_selectedIndex)) // handles the case of a single item
|
|
||||||
isEmpty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmpty != m_isEmpty) {
|
if (isEmpty != m_isEmpty) {
|
||||||
@@ -177,7 +181,6 @@ void MaterialBrowserTexturesModel::setTextures(const QList<ModelNode> &textures)
|
|||||||
emit isEmptyChanged();
|
emit isEmptyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectedTexture();
|
|
||||||
resetModel();
|
resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,9 +210,18 @@ void MaterialBrowserTexturesModel::addNewTexture()
|
|||||||
emit addNewTextureTriggered();
|
emit addNewTextureTriggered();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::deleteSelectedTexture()
|
void MaterialBrowserTexturesModel::deleteSelectedTextures()
|
||||||
{
|
{
|
||||||
deleteTexture(m_selectedIndex);
|
m_view->executeInTransaction(__FUNCTION__, [this] {
|
||||||
|
QStack<int> selectedIndexes;
|
||||||
|
for (int i = 0; i < m_textureList.size(); ++i) {
|
||||||
|
if (m_textureList.at(i).isSelected())
|
||||||
|
selectedIndexes << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!selectedIndexes.isEmpty())
|
||||||
|
deleteTexture(selectedIndexes.pop());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::updateTextureSource(const ModelNode &texture)
|
void MaterialBrowserTexturesModel::updateTextureSource(const ModelNode &texture)
|
||||||
@@ -238,14 +250,22 @@ void MaterialBrowserTexturesModel::updateAllTexturesSources()
|
|||||||
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), {RoleTexSource, RoleTexToolTip});
|
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), {RoleTexSource, RoleTexToolTip});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::updateSelectedTexture()
|
void MaterialBrowserTexturesModel::notifySelectionChanges(const QList<ModelNode> &selectedNodes,
|
||||||
|
const QList<ModelNode> &deselectedNodes)
|
||||||
{
|
{
|
||||||
if (!m_textureList.isEmpty() && m_selectedIndex < 0) {
|
QList<int> indices;
|
||||||
ModelNode tex = Utils3D::selectedTexture(m_view);
|
indices.reserve(selectedNodes.size() + deselectedNodes.size());
|
||||||
m_selectedIndex = textureIndex(tex);
|
for (const ModelNode &node : selectedNodes)
|
||||||
}
|
indices.append(textureIndex(node));
|
||||||
|
|
||||||
selectTexture(m_selectedIndex, true);
|
for (const ModelNode &node : deselectedNodes)
|
||||||
|
indices.append(textureIndex(node));
|
||||||
|
|
||||||
|
using Bound = QPair<int, int>;
|
||||||
|
const QList<Bound> &bounds = MaterialBrowserView::getSortedBounds(indices);
|
||||||
|
|
||||||
|
for (const Bound &bound : bounds)
|
||||||
|
emit dataChanged(index(bound.first), index(bound.second), {Roles::RoleTexSelected});
|
||||||
}
|
}
|
||||||
|
|
||||||
int MaterialBrowserTexturesModel::textureIndex(const ModelNode &texture) const
|
int MaterialBrowserTexturesModel::textureIndex(const ModelNode &texture) const
|
||||||
@@ -261,11 +281,6 @@ ModelNode MaterialBrowserTexturesModel::textureAt(int idx) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelNode MaterialBrowserTexturesModel::selectedTexture() const
|
|
||||||
{
|
|
||||||
return textureAt(m_selectedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MaterialBrowserTexturesModel::hasSingleModelSelection() const
|
bool MaterialBrowserTexturesModel::hasSingleModelSelection() const
|
||||||
{
|
{
|
||||||
return m_hasSingleModelSelection;
|
return m_hasSingleModelSelection;
|
||||||
@@ -280,6 +295,11 @@ void MaterialBrowserTexturesModel::setHasSingleModelSelection(bool b)
|
|||||||
emit hasSingleModelSelectionChanged();
|
emit hasSingleModelSelectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MaterialBrowserTexturesModel::onlyMaterialsSelected() const
|
||||||
|
{
|
||||||
|
return m_onlyMaterialsSelected;
|
||||||
|
}
|
||||||
|
|
||||||
bool MaterialBrowserTexturesModel::hasSceneEnv() const
|
bool MaterialBrowserTexturesModel::hasSceneEnv() const
|
||||||
{
|
{
|
||||||
return m_hasSceneEnv;
|
return m_hasSceneEnv;
|
||||||
@@ -300,20 +320,18 @@ void MaterialBrowserTexturesModel::resetModel()
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::selectTexture(int idx, bool force)
|
void MaterialBrowserTexturesModel::selectTexture(int idx, bool appendTxt)
|
||||||
{
|
{
|
||||||
if (m_textureList.size() == 0) {
|
if (!isValidIndex(idx))
|
||||||
m_selectedIndex = -1;
|
|
||||||
emit selectedIndexChanged(m_selectedIndex);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
idx = std::max(0, std::min(idx, rowCount() - 1));
|
ModelNode texture = m_textureList.at(idx);
|
||||||
|
QTC_ASSERT(texture, return);
|
||||||
|
|
||||||
if (idx != m_selectedIndex || force) {
|
if (appendTxt)
|
||||||
m_selectedIndex = idx;
|
texture.view()->selectModelNode(texture);
|
||||||
emit selectedIndexChanged(idx);
|
else
|
||||||
}
|
texture.selectNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::duplicateTexture(int idx)
|
void MaterialBrowserTexturesModel::duplicateTexture(int idx)
|
||||||
@@ -369,6 +387,15 @@ void MaterialBrowserTexturesModel::updateSceneEnvState()
|
|||||||
emit updateSceneEnvStateRequested();
|
emit updateSceneEnvStateRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MaterialBrowserTexturesModel::updateSelectionState()
|
||||||
|
{
|
||||||
|
setHasSingleModelSelection(
|
||||||
|
m_view->hasSingleSelectedModelNode()
|
||||||
|
&& Utils3D::getMaterialOfModel(m_view->singleSelectedModelNode()).isValid());
|
||||||
|
|
||||||
|
setOnlyMaterialsSelected(Utils::allOf(m_view->selectedModelNodes(), isMaterial));
|
||||||
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::applyAsLightProbe(qint64 internalId)
|
void MaterialBrowserTexturesModel::applyAsLightProbe(qint64 internalId)
|
||||||
{
|
{
|
||||||
int idx = m_textureIndexHash.value(internalId);
|
int idx = m_textureIndexHash.value(internalId);
|
||||||
@@ -378,9 +405,4 @@ void MaterialBrowserTexturesModel::applyAsLightProbe(qint64 internalId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserTexturesModel::updateModelSelectionState()
|
|
||||||
{
|
|
||||||
emit updateModelSelectionStateRequested();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -17,8 +17,8 @@ class MaterialBrowserTexturesModel : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||||
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
|
|
||||||
Q_PROPERTY(bool hasSingleModelSelection READ hasSingleModelSelection NOTIFY hasSingleModelSelectionChanged)
|
Q_PROPERTY(bool hasSingleModelSelection READ hasSingleModelSelection NOTIFY hasSingleModelSelectionChanged)
|
||||||
|
Q_PROPERTY(bool onlyMaterialsSelected READ onlyMaterialsSelected NOTIFY onlyMaterialsSelectedChanged)
|
||||||
Q_PROPERTY(bool hasSceneEnv READ hasSceneEnv NOTIFY hasSceneEnvChanged)
|
Q_PROPERTY(bool hasSceneEnv READ hasSceneEnv NOTIFY hasSceneEnvChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -35,19 +35,20 @@ public:
|
|||||||
QList<ModelNode> textures() const;
|
QList<ModelNode> textures() const;
|
||||||
void setTextures(const QList<ModelNode> &textures);
|
void setTextures(const QList<ModelNode> &textures);
|
||||||
void removeTexture(const ModelNode &texture);
|
void removeTexture(const ModelNode &texture);
|
||||||
void deleteSelectedTexture();
|
void deleteSelectedTextures();
|
||||||
void updateSelectedTexture();
|
|
||||||
void updateTextureSource(const ModelNode &texture);
|
void updateTextureSource(const ModelNode &texture);
|
||||||
void updateTextureId(const ModelNode &texture);
|
void updateTextureId(const ModelNode &texture);
|
||||||
void updateTextureName(const ModelNode &texture);
|
void updateTextureName(const ModelNode &texture);
|
||||||
void updateAllTexturesSources();
|
void updateAllTexturesSources();
|
||||||
|
void notifySelectionChanges(const QList<ModelNode> &selectedNodes,
|
||||||
|
const QList<ModelNode> &deselectedNodes);
|
||||||
int textureIndex(const ModelNode &texture) const;
|
int textureIndex(const ModelNode &texture) const;
|
||||||
ModelNode textureAt(int idx) const;
|
ModelNode textureAt(int idx) const;
|
||||||
ModelNode selectedTexture() const;
|
|
||||||
|
|
||||||
bool hasSingleModelSelection() const;
|
bool hasSingleModelSelection() const;
|
||||||
void setHasSingleModelSelection(bool b);
|
void setHasSingleModelSelection(bool b);
|
||||||
|
|
||||||
|
bool onlyMaterialsSelected() const;
|
||||||
bool hasSceneEnv() const;
|
bool hasSceneEnv() const;
|
||||||
void setHasSceneEnv(bool b);
|
void setHasSceneEnv(bool b);
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ public:
|
|||||||
|
|
||||||
void resetModel();
|
void resetModel();
|
||||||
|
|
||||||
Q_INVOKABLE void selectTexture(int idx, bool force = false);
|
Q_INVOKABLE void selectTexture(int idx, bool appendTxt = false);
|
||||||
Q_INVOKABLE void addNewTexture();
|
Q_INVOKABLE void addNewTexture();
|
||||||
Q_INVOKABLE void duplicateTexture(int idx);
|
Q_INVOKABLE void duplicateTexture(int idx);
|
||||||
Q_INVOKABLE void deleteTexture(int idx);
|
Q_INVOKABLE void deleteTexture(int idx);
|
||||||
@@ -64,44 +65,45 @@ public:
|
|||||||
Q_INVOKABLE void applyToSelectedModel(qint64 internalId);
|
Q_INVOKABLE void applyToSelectedModel(qint64 internalId);
|
||||||
Q_INVOKABLE void openTextureEditor();
|
Q_INVOKABLE void openTextureEditor();
|
||||||
Q_INVOKABLE void updateSceneEnvState();
|
Q_INVOKABLE void updateSceneEnvState();
|
||||||
Q_INVOKABLE void updateModelSelectionState();
|
Q_INVOKABLE void updateSelectionState();
|
||||||
Q_INVOKABLE void applyAsLightProbe(qint64 internalId);
|
Q_INVOKABLE void applyAsLightProbe(qint64 internalId);
|
||||||
Q_INVOKABLE bool isVisible(int idx) const;
|
Q_INVOKABLE bool isVisible(int idx) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void isEmptyChanged();
|
void isEmptyChanged();
|
||||||
void hasSingleModelSelectionChanged();
|
void hasSingleModelSelectionChanged();
|
||||||
void selectedIndexChanged(int idx);
|
void onlyMaterialsSelectedChanged();
|
||||||
void duplicateTextureTriggered(const QmlDesigner::ModelNode &texture);
|
void duplicateTextureTriggered(const QmlDesigner::ModelNode &texture);
|
||||||
void applyToSelectedMaterialTriggered(const QmlDesigner::ModelNode &texture);
|
void applyToSelectedMaterialTriggered(const QmlDesigner::ModelNode &texture);
|
||||||
void applyToSelectedModelTriggered(const QmlDesigner::ModelNode &texture);
|
void applyToSelectedModelTriggered(const QmlDesigner::ModelNode &texture);
|
||||||
void addNewTextureTriggered();
|
void addNewTextureTriggered();
|
||||||
void updateSceneEnvStateRequested();
|
void updateSceneEnvStateRequested();
|
||||||
void updateModelSelectionStateRequested();
|
|
||||||
void hasSceneEnvChanged();
|
void hasSceneEnvChanged();
|
||||||
void applyAsLightProbeRequested(const QmlDesigner::ModelNode &texture);
|
void applyAsLightProbeRequested(const QmlDesigner::ModelNode &texture);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isValidIndex(int idx) const;
|
bool isValidIndex(int idx) const;
|
||||||
|
void setOnlyMaterialsSelected(bool value);
|
||||||
|
|
||||||
QString m_searchText;
|
QString m_searchText;
|
||||||
QList<ModelNode> m_textureList;
|
QList<ModelNode> m_textureList;
|
||||||
QHash<qint32, int> m_textureIndexHash; // internalId -> index
|
QHash<qint32, int> m_textureIndexHash; // internalId -> index
|
||||||
|
|
||||||
int m_selectedIndex = 0;
|
|
||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
bool m_hasSingleModelSelection = false;
|
bool m_hasSingleModelSelection = false;
|
||||||
|
bool m_onlyMaterialsSelected = false;
|
||||||
bool m_hasSceneEnv = false;
|
bool m_hasSceneEnv = false;
|
||||||
|
|
||||||
QPointer<MaterialBrowserView> m_view;
|
QPointer<MaterialBrowserView> m_view;
|
||||||
|
|
||||||
enum {
|
enum Roles {
|
||||||
RoleTexHasDynamicProps = Qt::UserRole + 1,
|
RoleTexHasDynamicProps = Qt::UserRole + 1,
|
||||||
RoleTexInternalId,
|
RoleTexInternalId,
|
||||||
RoleTexName,
|
RoleTexName,
|
||||||
RoleTexSource,
|
RoleTexSource,
|
||||||
RoleTexToolTip,
|
RoleTexToolTip,
|
||||||
RoleTexVisible
|
RoleMatchedSearch,
|
||||||
|
RoleTexSelected,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -36,6 +36,16 @@
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
static bool isMaterial(const ModelNode &node)
|
||||||
|
{
|
||||||
|
return node.metaInfo().isQtQuick3DMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTexture(const ModelNode &node)
|
||||||
|
{
|
||||||
|
return node.metaInfo().isQtQuick3DTexture();
|
||||||
|
}
|
||||||
|
|
||||||
static QString propertyEditorResourcesPath()
|
static QString propertyEditorResourcesPath()
|
||||||
{
|
{
|
||||||
#ifdef SHARE_QML_PATH
|
#ifdef SHARE_QML_PATH
|
||||||
@@ -52,9 +62,6 @@ MaterialBrowserView::MaterialBrowserView(AsynchronousImageCache &imageCache,
|
|||||||
{
|
{
|
||||||
m_previewTimer.setSingleShot(true);
|
m_previewTimer.setSingleShot(true);
|
||||||
connect(&m_previewTimer, &QTimer::timeout, this, &MaterialBrowserView::requestPreviews);
|
connect(&m_previewTimer, &QTimer::timeout, this, &MaterialBrowserView::requestPreviews);
|
||||||
|
|
||||||
m_selectionTimer.setSingleShot(true);
|
|
||||||
connect(&m_selectionTimer, &QTimer::timeout, this, &MaterialBrowserView::handleModelSelectionChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialBrowserView::~MaterialBrowserView()
|
MaterialBrowserView::~MaterialBrowserView()
|
||||||
@@ -73,13 +80,6 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
// custom notifications below are sent to the MaterialEditor
|
// custom notifications below are sent to the MaterialEditor
|
||||||
MaterialBrowserModel *matBrowserModel = m_widget->materialBrowserModel().data();
|
MaterialBrowserModel *matBrowserModel = m_widget->materialBrowserModel().data();
|
||||||
|
|
||||||
connect(matBrowserModel, &MaterialBrowserModel::selectedIndexChanged, this, [this](int idx) {
|
|
||||||
if (!model())
|
|
||||||
return;
|
|
||||||
m_pendingMaterialIndex = idx;
|
|
||||||
m_selectionTimer.start(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(matBrowserModel, &MaterialBrowserModel::applyToSelectedTriggered, this,
|
connect(matBrowserModel, &MaterialBrowserModel::applyToSelectedTriggered, this,
|
||||||
[&] (const ModelNode &material, bool add) {
|
[&] (const ModelNode &material, bool add) {
|
||||||
Utils3D::applyMaterialToModels(this, material, Utils3D::getSelectedModels(this), add);
|
Utils3D::applyMaterialToModels(this, material, Utils3D::getSelectedModels(this), add);
|
||||||
@@ -174,12 +174,6 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
|
|
||||||
// custom notifications below are sent to the TextureEditor
|
// custom notifications below are sent to the TextureEditor
|
||||||
MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data();
|
MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data();
|
||||||
connect(texturesModel, &MaterialBrowserTexturesModel::selectedIndexChanged, this, [this](int idx) {
|
|
||||||
if (!model())
|
|
||||||
return;
|
|
||||||
m_pendingTextureIndex = idx;
|
|
||||||
m_selectionTimer.start(0);
|
|
||||||
});
|
|
||||||
connect(texturesModel, &MaterialBrowserTexturesModel::duplicateTextureTriggered, this,
|
connect(texturesModel, &MaterialBrowserTexturesModel::duplicateTextureTriggered, this,
|
||||||
[&] (const ModelNode &texture) {
|
[&] (const ModelNode &texture) {
|
||||||
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("TextureEditor");
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("TextureEditor");
|
||||||
@@ -190,8 +184,9 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
[&] (const ModelNode &texture) {
|
[&] (const ModelNode &texture) {
|
||||||
if (!m_widget)
|
if (!m_widget)
|
||||||
return;
|
return;
|
||||||
const ModelNode material = m_widget->materialBrowserModel()->selectedMaterial();
|
|
||||||
applyTextureToMaterial({material}, texture);
|
ModelNodes materialNodes = Utils3D::getSelectedMaterials(this);
|
||||||
|
applyTextureToMaterial(materialNodes, texture);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(texturesModel, &MaterialBrowserTexturesModel::applyToSelectedModelTriggered, this,
|
connect(texturesModel, &MaterialBrowserTexturesModel::applyToSelectedModelTriggered, this,
|
||||||
@@ -214,14 +209,6 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
|||||||
m_widget->materialBrowserTexturesModel()->setHasSceneEnv(sceneEnvExists);
|
m_widget->materialBrowserTexturesModel()->setHasSceneEnv(sceneEnvExists);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(texturesModel, &MaterialBrowserTexturesModel::updateModelSelectionStateRequested, this, [this] {
|
|
||||||
bool hasModel = false;
|
|
||||||
const QList<ModelNode> selectedModels = Utils3D::getSelectedModels(this);
|
|
||||||
if (selectedModels.size() == 1)
|
|
||||||
hasModel = Utils3D::getMaterialOfModel(selectedModels.at(0)).isValid();
|
|
||||||
m_widget->materialBrowserTexturesModel()->setHasSingleModelSelection(hasModel);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(texturesModel,
|
connect(texturesModel,
|
||||||
&MaterialBrowserTexturesModel::applyAsLightProbeRequested,
|
&MaterialBrowserTexturesModel::applyAsLightProbeRequested,
|
||||||
this,
|
this,
|
||||||
@@ -290,9 +277,6 @@ void MaterialBrowserView::refreshModel(bool updateImages)
|
|||||||
|
|
||||||
if (updateImages)
|
if (updateImages)
|
||||||
updateMaterialsPreview();
|
updateMaterialsPreview();
|
||||||
|
|
||||||
updateMaterialSelection();
|
|
||||||
updateTextureSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::updateMaterialsPreview()
|
void MaterialBrowserView::updateMaterialsPreview()
|
||||||
@@ -315,13 +299,12 @@ void MaterialBrowserView::updatePropertyList(const QList<T> &propertyList)
|
|||||||
else
|
else
|
||||||
m_previewRequests << node;
|
m_previewRequests << node;
|
||||||
} else if (isTexture(node)) {
|
} else if (isTexture(node)) {
|
||||||
QmlObjectNode selectedTex = m_widget->materialBrowserTexturesModel()->selectedTexture();
|
|
||||||
if (property.name() == "source")
|
if (property.name() == "source")
|
||||||
m_widget->materialBrowserTexturesModel()->updateTextureSource(node);
|
m_widget->materialBrowserTexturesModel()->updateTextureSource(node);
|
||||||
else if (property.name() == "objectName")
|
else if (property.name() == "objectName")
|
||||||
m_widget->materialBrowserTexturesModel()->updateTextureName(node);
|
m_widget->materialBrowserTexturesModel()->updateTextureName(node);
|
||||||
} else {
|
} else {
|
||||||
QmlObjectNode selectedTex = m_widget->materialBrowserTexturesModel()->selectedTexture();
|
QmlObjectNode selectedTex = Utils3D::selectedTexture(this);
|
||||||
if (property.name() == "source" && selectedTex.propertyChangeForCurrentState() == node)
|
if (property.name() == "source" && selectedTex.propertyChangeForCurrentState() == node)
|
||||||
m_widget->materialBrowserTexturesModel()->updateTextureSource(selectedTex);
|
m_widget->materialBrowserTexturesModel()->updateTextureSource(selectedTex);
|
||||||
}
|
}
|
||||||
@@ -331,26 +314,11 @@ void MaterialBrowserView::updatePropertyList(const QList<T> &propertyList)
|
|||||||
m_previewTimer.start(0);
|
m_previewTimer.start(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaterialBrowserView::isMaterial(const ModelNode &node) const
|
|
||||||
{
|
|
||||||
return node.metaInfo().isQtQuick3DMaterial();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MaterialBrowserView::isTexture(const ModelNode &node) const
|
|
||||||
{
|
|
||||||
if (!node.isValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return node.metaInfo().isQtQuick3DTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MaterialBrowserView::modelAboutToBeDetached(Model *model)
|
void MaterialBrowserView::modelAboutToBeDetached(Model *model)
|
||||||
{
|
{
|
||||||
m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport);
|
m_widget->materialBrowserModel()->setMaterials({}, m_hasQuick3DImport);
|
||||||
m_widget->materialBrowserModel()->setHasMaterialLibrary(false);
|
m_widget->materialBrowserModel()->setHasMaterialLibrary(false);
|
||||||
m_widget->clearPreviewCache();
|
m_widget->clearPreviewCache();
|
||||||
m_pendingMaterialIndex = -1;
|
|
||||||
m_pendingTextureIndex = -1;
|
|
||||||
|
|
||||||
if (m_propertyGroupsLoaded) {
|
if (m_propertyGroupsLoaded) {
|
||||||
m_propertyGroupsLoaded = false;
|
m_propertyGroupsLoaded = false;
|
||||||
@@ -363,25 +331,19 @@ void MaterialBrowserView::modelAboutToBeDetached(Model *model)
|
|||||||
void MaterialBrowserView::selectedNodesChanged([[maybe_unused]] const QList<ModelNode> &selectedNodeList,
|
void MaterialBrowserView::selectedNodesChanged([[maybe_unused]] const QList<ModelNode> &selectedNodeList,
|
||||||
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
|
[[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
|
||||||
{
|
{
|
||||||
const QList<ModelNode> selectedModels = Utils3D::getSelectedModels(this);
|
using namespace std::ranges;
|
||||||
|
|
||||||
m_widget->materialBrowserModel()->setHasModelSelection(!selectedModels.isEmpty());
|
ModelNodes selectedMaterials = Utils::filtered(selectedNodeList, isMaterial);
|
||||||
|
ModelNodes deselectedMaterials = Utils::filtered(lastSelectedNodeList, isMaterial);
|
||||||
|
|
||||||
// the logic below selects the material of the first selected model if auto selection is on
|
ModelNodes selectedTextures = Utils::filtered(selectedNodeList, isTexture);
|
||||||
if (!m_autoSelectModelMaterial)
|
ModelNodes deselectedTextures = Utils::filtered(lastSelectedNodeList, isTexture);
|
||||||
return;
|
|
||||||
|
|
||||||
if (selectedNodeList.size() > 1 || selectedModels.isEmpty())
|
m_widget->materialBrowserModel()->notifySelectionChanges(selectedMaterials, deselectedMaterials);
|
||||||
return;
|
m_widget->materialBrowserModel()->setHasModelSelection(!selectedMaterials.isEmpty());
|
||||||
|
|
||||||
ModelNode mat = Utils3D::getMaterialOfModel(selectedModels.at(0));
|
m_widget->materialBrowserTexturesModel()->notifySelectionChanges(selectedTextures,
|
||||||
|
deselectedTextures);
|
||||||
if (!mat.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// if selected object is a model, select its material in the material browser and editor
|
|
||||||
int idx = m_widget->materialBrowserModel()->materialIndex(mat);
|
|
||||||
m_widget->materialBrowserModel()->selectMaterial(idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::modelNodePreviewPixmapChanged(const ModelNode &node,
|
void MaterialBrowserView::modelNodePreviewPixmapChanged(const ModelNode &node,
|
||||||
@@ -450,12 +412,8 @@ void MaterialBrowserView::nodeReparented(const ModelNode &node,
|
|||||||
resetPuppet();
|
resetPuppet();
|
||||||
m_puppetResetPending = true;
|
m_puppetResetPending = true;
|
||||||
}
|
}
|
||||||
int idx = m_widget->materialBrowserModel()->materialIndex(node);
|
|
||||||
m_widget->materialBrowserModel()->selectMaterial(idx);
|
|
||||||
m_widget->materialBrowserModel()->refreshSearch();
|
m_widget->materialBrowserModel()->refreshSearch();
|
||||||
} else { // is texture
|
} else { // is texture
|
||||||
int idx = m_widget->materialBrowserTexturesModel()->textureIndex(node);
|
|
||||||
m_widget->materialBrowserTexturesModel()->selectTexture(idx);
|
|
||||||
m_widget->materialBrowserTexturesModel()->refreshSearch();
|
m_widget->materialBrowserTexturesModel()->refreshSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,17 +438,6 @@ void MaterialBrowserView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
|||||||
m_widget->materialBrowserTexturesModel()->removeTexture(removedNode);
|
m_widget->materialBrowserTexturesModel()->removeTexture(removedNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode,
|
|
||||||
const NodeAbstractProperty &parentProperty,
|
|
||||||
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
|
||||||
{
|
|
||||||
if (parentProperty.parentModelNode().id() != Constants::MATERIAL_LIB_ID)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_widget->materialBrowserModel()->updateSelectedMaterial();
|
|
||||||
m_widget->materialBrowserTexturesModel()->updateSelectedTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
|
void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
|
||||||
{
|
{
|
||||||
if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model())
|
if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model())
|
||||||
@@ -515,59 +462,6 @@ void MaterialBrowserView::requestPreviews()
|
|||||||
m_previewRequests.clear();
|
m_previewRequests.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::updateMaterialSelection()
|
|
||||||
{
|
|
||||||
QTC_ASSERT(model(), return);
|
|
||||||
|
|
||||||
ModelNode node = Utils3D::selectedMaterial(this);
|
|
||||||
int idx = m_widget->materialBrowserModel()->materialIndex(node);
|
|
||||||
if (idx == -1 && !m_widget->materialBrowserModel()->isEmpty())
|
|
||||||
idx = 0;
|
|
||||||
if (idx != -1) {
|
|
||||||
m_widget->materialBrowserModel()->selectMaterial(idx);
|
|
||||||
m_widget->focusMaterialSection(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MaterialBrowserView::updateTextureSelection()
|
|
||||||
{
|
|
||||||
QTC_ASSERT(model(), return);
|
|
||||||
|
|
||||||
ModelNode node = Utils3D::selectedTexture(this);
|
|
||||||
int idx = m_widget->materialBrowserTexturesModel()->textureIndex(node);
|
|
||||||
if (idx == -1 && !m_widget->materialBrowserTexturesModel()->isEmpty())
|
|
||||||
idx = 0;
|
|
||||||
if (idx != -1) {
|
|
||||||
m_widget->materialBrowserTexturesModel()->selectTexture(idx);
|
|
||||||
m_widget->materialBrowserTexturesModel()->refreshSearch();
|
|
||||||
m_widget->focusMaterialSection(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is asynchronously called in response to a selection change in the material and
|
|
||||||
// texture models to update the selection state to the main scene model.
|
|
||||||
void MaterialBrowserView::handleModelSelectionChange()
|
|
||||||
{
|
|
||||||
if (!model())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_pendingMaterialIndex >= 0) {
|
|
||||||
ModelNode matNode = m_widget->materialBrowserModel()->materialAt(m_pendingMaterialIndex);
|
|
||||||
ModelNode current = Utils3D::selectedMaterial(this);
|
|
||||||
if (current != matNode)
|
|
||||||
Utils3D::selectMaterial(matNode);
|
|
||||||
m_pendingMaterialIndex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pendingTextureIndex >= 0) {
|
|
||||||
ModelNode texNode = m_widget->materialBrowserTexturesModel()->textureAt(m_pendingTextureIndex);
|
|
||||||
ModelNode current = Utils3D::selectedTexture(this);
|
|
||||||
if (current != texNode)
|
|
||||||
Utils3D::selectTexture(texNode);
|
|
||||||
m_pendingTextureIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MaterialBrowserView::importsChanged([[maybe_unused]] const Imports &addedImports,
|
void MaterialBrowserView::importsChanged([[maybe_unused]] const Imports &addedImports,
|
||||||
[[maybe_unused]] const Imports &removedImports)
|
[[maybe_unused]] const Imports &removedImports)
|
||||||
{
|
{
|
||||||
@@ -597,7 +491,7 @@ void MaterialBrowserView::customNotification(const AbstractView *view,
|
|||||||
refreshModel(true);
|
refreshModel(true);
|
||||||
});
|
});
|
||||||
} else if (identifier == "delete_selected_material") {
|
} else if (identifier == "delete_selected_material") {
|
||||||
m_widget->deleteSelectedItem();
|
m_widget->deleteSelectedItems();
|
||||||
} else if (identifier == "apply_asset_to_model3D") {
|
} else if (identifier == "apply_asset_to_model3D") {
|
||||||
m_appliedTexturePath = data.at(0).toString();
|
m_appliedTexturePath = data.at(0).toString();
|
||||||
applyTextureToModel3D(nodeList.at(0));
|
applyTextureToModel3D(nodeList.at(0));
|
||||||
@@ -643,7 +537,7 @@ void MaterialBrowserView::instancePropertyChanged(const QList<QPair<ModelNode, P
|
|||||||
{
|
{
|
||||||
for (const auto &nodeProp : propertyList) {
|
for (const auto &nodeProp : propertyList) {
|
||||||
ModelNode node = nodeProp.first;
|
ModelNode node = nodeProp.first;
|
||||||
if (node.metaInfo().isQtQuick3DMaterial())
|
if (isMaterial(node))
|
||||||
m_previewRequests.insert(node);
|
m_previewRequests.insert(node);
|
||||||
}
|
}
|
||||||
if (!m_previewRequests.isEmpty() && !m_previewTimer.isActive()) {
|
if (!m_previewRequests.isEmpty() && !m_previewTimer.isActive()) {
|
||||||
@@ -659,10 +553,6 @@ void MaterialBrowserView::auxiliaryDataChanged(const ModelNode &,
|
|||||||
{
|
{
|
||||||
if (type == Utils3D::active3dSceneProperty)
|
if (type == Utils3D::active3dSceneProperty)
|
||||||
active3DSceneChanged(data.toInt());
|
active3DSceneChanged(data.toInt());
|
||||||
else if (type == Utils3D::matLibSelectedMaterialProperty)
|
|
||||||
updateMaterialSelection();
|
|
||||||
else if ( type == Utils3D::matLibSelectedTextureProperty)
|
|
||||||
updateTextureSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserView::applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture)
|
void MaterialBrowserView::applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture)
|
||||||
@@ -762,6 +652,49 @@ void MaterialBrowserView::closeChooseMatPropsView()
|
|||||||
m_chooseMatPropsView->close();
|
m_chooseMatPropsView->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Gets a list of subranges which covers the input list
|
||||||
|
* Each subrange will be extended until reaches a gap.
|
||||||
|
* A gap is defined as a range that is not included in the input list.
|
||||||
|
* Minimum length of the gap should be 2, since 1 is considered as a
|
||||||
|
* continuous range.
|
||||||
|
* \param values: unsorted integer list
|
||||||
|
* \return A sorted list of closed subranges. Each pair consists of two
|
||||||
|
* numbers. The first number is the start of the subrange, and the second
|
||||||
|
* number is the end of subrange which is available in the values.
|
||||||
|
*/
|
||||||
|
QList<QPair<int, int>> MaterialBrowserView::getSortedBounds(const QList<int> &values)
|
||||||
|
{
|
||||||
|
using Bound = QPair<int, int>;
|
||||||
|
QList<int> sortedValues = Utils::sorted(values);
|
||||||
|
|
||||||
|
Bound tempBound;
|
||||||
|
QList<Bound> bounds;
|
||||||
|
bounds.reserve(sortedValues.size());
|
||||||
|
|
||||||
|
if (!sortedValues.isEmpty()) {
|
||||||
|
tempBound.first = sortedValues.first();
|
||||||
|
tempBound.second = sortedValues.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int value : std::as_const(sortedValues)) {
|
||||||
|
// If the difference is more than 1, a gap is found.
|
||||||
|
// We need to close the previous subrange, and start a new one
|
||||||
|
if (value - tempBound.second > 1) {
|
||||||
|
bounds << tempBound;
|
||||||
|
tempBound.first = value;
|
||||||
|
}
|
||||||
|
tempBound.second = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sortedValues.isEmpty())
|
||||||
|
bounds << tempBound;
|
||||||
|
|
||||||
|
bounds.shrink_to_fit();
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
bool MaterialBrowserView::eventFilter(QObject *obj, QEvent *event)
|
bool MaterialBrowserView::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if (event->type() == QEvent::KeyPress) {
|
if (event->type() == QEvent::KeyPress) {
|
||||||
|
@@ -47,8 +47,6 @@ public:
|
|||||||
const NodeAbstractProperty &oldPropertyParent,
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
AbstractView::PropertyChangeFlags propertyChange) override;
|
AbstractView::PropertyChangeFlags propertyChange) override;
|
||||||
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
||||||
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
|
|
||||||
PropertyChangeFlags propertyChange) override;
|
|
||||||
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
|
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
|
||||||
void customNotification(const AbstractView *view, const QString &identifier,
|
void customNotification(const AbstractView *view, const QString &identifier,
|
||||||
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
|
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
|
||||||
@@ -68,6 +66,8 @@ public:
|
|||||||
Q_INVOKABLE void applyTextureToProperty(const QString &matId, const QString &propName);
|
Q_INVOKABLE void applyTextureToProperty(const QString &matId, const QString &propName);
|
||||||
Q_INVOKABLE void closeChooseMatPropsView();
|
Q_INVOKABLE void closeChooseMatPropsView();
|
||||||
|
|
||||||
|
static QList<QPair<int, int>> getSortedBounds(const QList<int> &values);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
@@ -79,33 +79,24 @@ private:
|
|||||||
template<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty, T>::value>::type>
|
template<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty, T>::value>::type>
|
||||||
void updatePropertyList(const QList<T> &propertyList);
|
void updatePropertyList(const QList<T> &propertyList);
|
||||||
|
|
||||||
bool isMaterial(const ModelNode &node) const;
|
|
||||||
bool isTexture(const ModelNode &node) const;
|
|
||||||
void loadPropertyGroups();
|
void loadPropertyGroups();
|
||||||
void requestPreviews();
|
void requestPreviews();
|
||||||
ModelNode resolveSceneEnv();
|
ModelNode resolveSceneEnv();
|
||||||
void updateMaterialSelection();
|
|
||||||
void updateTextureSelection();
|
|
||||||
void handleModelSelectionChange();
|
|
||||||
|
|
||||||
AsynchronousImageCache &m_imageCache;
|
AsynchronousImageCache &m_imageCache;
|
||||||
QPointer<MaterialBrowserWidget> m_widget;
|
QPointer<MaterialBrowserWidget> m_widget;
|
||||||
|
|
||||||
bool m_hasQuick3DImport = false;
|
bool m_hasQuick3DImport = false;
|
||||||
bool m_autoSelectModelMaterial = false; // TODO: wire this to some action
|
|
||||||
bool m_puppetResetPending = false;
|
bool m_puppetResetPending = false;
|
||||||
bool m_propertyGroupsLoaded = false;
|
bool m_propertyGroupsLoaded = false;
|
||||||
|
|
||||||
QTimer m_previewTimer;
|
QTimer m_previewTimer;
|
||||||
QTimer m_selectionTimer; // Compress selection and avoid illegal callbacks to model
|
|
||||||
QSet<ModelNode> m_previewRequests;
|
QSet<ModelNode> m_previewRequests;
|
||||||
QPointer<QQuickView> m_chooseMatPropsView;
|
QPointer<QQuickView> m_chooseMatPropsView;
|
||||||
QHash<QString, QList<PropertyName>> m_textureModels;
|
QHash<QString, QList<PropertyName>> m_textureModels;
|
||||||
QString m_appliedTextureId;
|
QString m_appliedTextureId;
|
||||||
QString m_appliedTexturePath; // defers texture creation until dialog apply
|
QString m_appliedTexturePath; // defers texture creation until dialog apply
|
||||||
int m_sceneId = -1;
|
int m_sceneId = -1;
|
||||||
int m_pendingMaterialIndex = -1;
|
|
||||||
int m_pendingTextureIndex = -1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -237,12 +237,12 @@ void MaterialBrowserWidget::updateMaterialPreview(const ModelNode &node, const Q
|
|||||||
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "refreshPreview", Q_ARG(QVariant, idx));
|
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "refreshPreview", Q_ARG(QVariant, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserWidget::deleteSelectedItem()
|
void MaterialBrowserWidget::deleteSelectedItems()
|
||||||
{
|
{
|
||||||
if (m_materialSectionFocused)
|
m_materialBrowserView->executeInTransaction(__FUNCTION__, [this] {
|
||||||
m_materialBrowserModel->deleteSelectedMaterial();
|
m_materialBrowserModel->deleteSelectedMaterials();
|
||||||
else
|
m_materialBrowserTexturesModel->deleteSelectedTextures();
|
||||||
m_materialBrowserTexturesModel->deleteSelectedTexture();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QToolButton *> MaterialBrowserWidget::createToolBarWidgets()
|
QList<QToolButton *> MaterialBrowserWidget::createToolBarWidgets()
|
||||||
@@ -312,7 +312,7 @@ void MaterialBrowserWidget::acceptBundleTextureDropOnMaterial(int matIndex, cons
|
|||||||
ModelNode tex = CreateTexture(m_materialBrowserView).execute(bundleTexPath.toLocalFile());
|
ModelNode tex = CreateTexture(m_materialBrowserView).execute(bundleTexPath.toLocalFile());
|
||||||
QTC_ASSERT(tex.isValid(), return);
|
QTC_ASSERT(tex.isValid(), return);
|
||||||
|
|
||||||
m_materialBrowserModel->selectMaterial(matIndex);
|
mat.model()->setSelectedModelNodes({mat});
|
||||||
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ void MaterialBrowserWidget::acceptAssetsDropOnMaterial(int matIndex, const QList
|
|||||||
ModelNode tex = CreateTexture(m_materialBrowserView).execute(imageSrc);
|
ModelNode tex = CreateTexture(m_materialBrowserView).execute(imageSrc);
|
||||||
QTC_ASSERT(tex.isValid(), return);
|
QTC_ASSERT(tex.isValid(), return);
|
||||||
|
|
||||||
m_materialBrowserModel->selectMaterial(matIndex);
|
mat.model()->setSelectedModelNodes({mat});
|
||||||
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -355,7 +355,7 @@ void MaterialBrowserWidget::acceptTextureDropOnMaterial(int matIndex, const QStr
|
|||||||
ModelNode tex = m_materialBrowserView->modelNodeForInternalId(texId.toInt());
|
ModelNode tex = m_materialBrowserView->modelNodeForInternalId(texId.toInt());
|
||||||
|
|
||||||
if (mat.isValid() && tex.isValid()) {
|
if (mat.isValid() && tex.isValid()) {
|
||||||
m_materialBrowserModel->selectMaterial(matIndex);
|
mat.model()->setSelectedModelNodes({mat});
|
||||||
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,10 +371,10 @@ void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserWidget::addMaterialToContentLibrary()
|
void MaterialBrowserWidget::addMaterialToContentLibrary(const QVariant &material)
|
||||||
{
|
{
|
||||||
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("ContentLibrary");
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("ContentLibrary");
|
||||||
ModelNode mat = m_materialBrowserModel->selectedMaterial();
|
ModelNode mat = material.value<ModelNode>();
|
||||||
m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat},
|
m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat},
|
||||||
{m_previewImageProvider->getPixmap(mat)}); // to ContentLibrary
|
{m_previewImageProvider->getPixmap(mat)}); // to ContentLibrary
|
||||||
}
|
}
|
||||||
@@ -384,10 +384,13 @@ void MaterialBrowserWidget::importMaterial()
|
|||||||
m_bundleHelper->importBundleToProject();
|
m_bundleHelper->importBundleToProject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserWidget::exportMaterial()
|
void MaterialBrowserWidget::exportMaterial(int idx)
|
||||||
{
|
{
|
||||||
ModelNode mat = m_materialBrowserModel->selectedMaterial();
|
ModelNode mat = m_materialBrowserModel->materialAt(idx);
|
||||||
|
QTC_ASSERT(mat, return);
|
||||||
|
|
||||||
m_bundleHelper->exportBundle({mat}, m_previewImageProvider->getPixmap(mat));
|
m_bundleHelper->exportBundle({mat}, m_previewImageProvider->getPixmap(mat));
|
||||||
|
m_materialBrowserModel->updateMaterialComponent(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialBrowserWidget::addQtQuick3D()
|
void MaterialBrowserWidget::addQtQuick3D()
|
||||||
|
@@ -50,7 +50,7 @@ public:
|
|||||||
QPointer<MaterialBrowserModel> materialBrowserModel() const;
|
QPointer<MaterialBrowserModel> materialBrowserModel() const;
|
||||||
QPointer<MaterialBrowserTexturesModel> materialBrowserTexturesModel() const;
|
QPointer<MaterialBrowserTexturesModel> materialBrowserTexturesModel() const;
|
||||||
void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap);
|
void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap);
|
||||||
void deleteSelectedItem();
|
void deleteSelectedItems();
|
||||||
|
|
||||||
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
|
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
|
||||||
Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos);
|
Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos);
|
||||||
@@ -63,9 +63,9 @@ public:
|
|||||||
Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls);
|
Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls);
|
||||||
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
|
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
|
||||||
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
|
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
|
||||||
Q_INVOKABLE void addMaterialToContentLibrary();
|
Q_INVOKABLE void addMaterialToContentLibrary(const QVariant &material);
|
||||||
Q_INVOKABLE void importMaterial();
|
Q_INVOKABLE void importMaterial();
|
||||||
Q_INVOKABLE void exportMaterial();
|
Q_INVOKABLE void exportMaterial(int idx);
|
||||||
Q_INVOKABLE void addQtQuick3D();
|
Q_INVOKABLE void addQtQuick3D();
|
||||||
|
|
||||||
StudioQuickWidget *quickWidget() const;
|
StudioQuickWidget *quickWidget() const;
|
||||||
|
Reference in New Issue
Block a user