forked from qt-creator/qt-creator
QmlDesigner: Add dynamic properties to property editor
Added dynamic properties section to property editor and material editor for all nodes. It shows all dynamic properties defined for the node with proper editors for the supported types. Dynamic properties can be added and removed via property editor as well. Material editor shows dynamic properties similarly. Fixes: QDS-7411 Change-Id: Id195f5ca23d55544cea29eb8996690a7eed1cc57 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -63,6 +63,10 @@ PropertyEditorPane {
|
||||
|
||||
Item { width: 1; height: 10 }
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: MaterialEditorDynamicPropertiesModel {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specificsTwo
|
||||
|
||||
@@ -79,7 +83,11 @@ PropertyEditorPane {
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: 1; height: 10 }
|
||||
Item {
|
||||
width: 1
|
||||
height: 10
|
||||
visible: specificsTwo.visible
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specificsOne
|
||||
|
@@ -37,6 +37,10 @@ PropertyEditorPane {
|
||||
showState: true
|
||||
}
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
}
|
||||
|
||||
GeometrySection {}
|
||||
|
||||
Section {
|
||||
|
@@ -40,6 +40,10 @@ PropertyEditorPane {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: specificsTwo
|
||||
anchors.left: parent.left
|
||||
|
@@ -66,6 +66,10 @@ SecondColumnLayout {
|
||||
colorEditor.backendValue.resetValue()
|
||||
}
|
||||
|
||||
function initEditor() {
|
||||
cePopup.initEditor()
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: backendConnection
|
||||
target: colorEditor
|
||||
|
@@ -44,6 +44,23 @@ T.Popup {
|
||||
|
||||
property bool isInValidState: false
|
||||
|
||||
function initEditor() {
|
||||
if (colorEditor.supportGradient && gradientModel.hasGradient) {
|
||||
colorEditor.color = gradientLine.currentColor
|
||||
gradientLine.currentColor = colorEditor.color
|
||||
hexTextField.text = colorEditor.color
|
||||
popupHexTextField.text = colorEditor.color
|
||||
}
|
||||
|
||||
cePopup.isInValidState = true
|
||||
colorEditor.originalColor = colorEditor.color
|
||||
colorPalette.selectedColor = colorEditor.color
|
||||
colorPicker.color = colorEditor.color
|
||||
|
||||
cePopup.createModel()
|
||||
cePopup.determineActiveColorMode()
|
||||
}
|
||||
|
||||
function commitGradientColor() {
|
||||
var hexColor = convertColorToString(colorEditor.color)
|
||||
cePopup.popupHexTextField.text = hexColor
|
||||
@@ -475,24 +492,10 @@ T.Popup {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelNodeBackend
|
||||
function onSelectionChanged() {
|
||||
if (colorEditor.supportGradient && gradientModel.hasGradient) {
|
||||
colorEditor.color = gradientLine.currentColor
|
||||
gradientLine.currentColor = colorEditor.color
|
||||
hexTextField.text = colorEditor.color
|
||||
popupHexTextField.text = colorEditor.color
|
||||
}
|
||||
|
||||
cePopup.isInValidState = true
|
||||
colorEditor.originalColor = colorEditor.color
|
||||
colorPalette.selectedColor = colorEditor.color
|
||||
colorPicker.color = colorEditor.color
|
||||
|
||||
cePopup.createModel()
|
||||
cePopup.determineActiveColorMode()
|
||||
cePopup.initEditor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,772 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import HelperWidgets 2.0
|
||||
import QtQuick.Templates 2.15 as T
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Section {
|
||||
id: root
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
caption: qsTr("User Added Properties")
|
||||
|
||||
property DynamicPropertiesModel propertiesModel: null
|
||||
|
||||
property Component colorEditor: Component {
|
||||
id: colorEditor
|
||||
ColorEditor {
|
||||
id: colorEditorControl
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
supportGradient: false
|
||||
spacer.visible: false
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap }
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: colorEditorControl.remove()
|
||||
}
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component intEditor: Component {
|
||||
id: intEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutInt
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
SpinBox {
|
||||
maximumValue: 9999999
|
||||
minimumValue: -9999999
|
||||
backendValue: layoutInt.backendValue
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
Item {
|
||||
height: 10
|
||||
implicitWidth: {
|
||||
return StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutInt.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component realEditor: Component {
|
||||
id: realEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutReal
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
SpinBox {
|
||||
backendValue: layoutReal.backendValue
|
||||
minimumValue: -9999999
|
||||
maximumValue: 9999999
|
||||
decimals: 2
|
||||
stepSize: 0.1
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
Item {
|
||||
height: 10
|
||||
implicitWidth: {
|
||||
return StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutReal.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component stringEditor: Component {
|
||||
id: stringEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutString
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
LineEdit {
|
||||
backendValue: layoutString.backendValue
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutString.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component boolEditor: Component {
|
||||
id: boolEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutBool
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
CheckBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
text: layoutBool.backendValue.value
|
||||
backendValue: layoutBool.backendValue
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
Item {
|
||||
height: 10
|
||||
implicitWidth: {
|
||||
return StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutBool.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component urlEditor: Component {
|
||||
id: urlEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutUrl
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
UrlChooser {
|
||||
backendValue: layoutUrl.backendValue
|
||||
comboBox.implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
spacer.implicitWidth: StudioTheme.Values.controlLabelGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutUrl.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component aliasEditor: Component {
|
||||
id: aliasEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutAlias
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
property alias lineEdit: lineEdit
|
||||
|
||||
signal remove
|
||||
|
||||
function updateLineEditText() {
|
||||
lineEdit.text = lineEdit.backendValue.expression
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
id: lineEdit
|
||||
backendValue: layoutAlias.backendValue
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
writeAsExpression: true
|
||||
showTranslateCheckBox: false
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutAlias.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component textureInputEditor: Component {
|
||||
id: textureInputEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutTextureInput
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
ItemFilterComboBox {
|
||||
typeFilter: "QtQuick3D.TextureInput"
|
||||
validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
|
||||
backendValue: layoutTextureInput.backendValue
|
||||
implicitWidth: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutTextureInput.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
property Component vectorEditor: Component {
|
||||
id: vectorEditor
|
||||
ColumnLayout {
|
||||
id: layoutVector
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
property int vecSize: 0
|
||||
property var proxyValues: []
|
||||
property var spinBoxes: [boxX, boxY, boxZ, boxW]
|
||||
|
||||
signal remove
|
||||
|
||||
onVecSizeChanged: updateProxyValues()
|
||||
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
function isValidValue(v) {
|
||||
return !(v === undefined || isNaN(v))
|
||||
}
|
||||
|
||||
function updateExpression() {
|
||||
for (let i = 0; i < vecSize; ++i) {
|
||||
if (!isValidValue(proxyValues[i].value))
|
||||
return
|
||||
}
|
||||
|
||||
let expStr = "Qt.vector" + vecSize + "d("+proxyValues[0].value
|
||||
for (let j=1; j < vecSize; ++j)
|
||||
expStr += ", " + proxyValues[j].value
|
||||
expStr += ")"
|
||||
|
||||
layoutVector.backendValue.expression = expStr
|
||||
}
|
||||
|
||||
function updateProxyValues() {
|
||||
if (!backendValue)
|
||||
return;
|
||||
|
||||
const startIndex = backendValue.expression.indexOf('(')
|
||||
const endIndex = backendValue.expression.indexOf(')')
|
||||
if (startIndex === -1 || endIndex === -1 || endIndex < startIndex)
|
||||
return
|
||||
const numberStr = backendValue.expression.slice(startIndex + 1, endIndex)
|
||||
const numbers = numberStr.split(",")
|
||||
if (!Array.isArray(numbers) || numbers.length !== vecSize)
|
||||
return
|
||||
|
||||
let vals = []
|
||||
for (let i = 0; i < vecSize; ++i) {
|
||||
vals[i] = parseFloat(numbers[i])
|
||||
if (!isValidValue(vals[i]))
|
||||
return
|
||||
}
|
||||
|
||||
for (let j = 0; j < vecSize; ++j)
|
||||
proxyValues[j].value = vals[j]
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
id: boxX
|
||||
minimumValue: -9999999
|
||||
maximumValue: 9999999
|
||||
decimals: 2
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
text: "X"
|
||||
tooltip: "X"
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
SpinBox {
|
||||
id: boxY
|
||||
minimumValue: -9999999
|
||||
maximumValue: 9999999
|
||||
decimals: 2
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
text: "Y"
|
||||
tooltip: "Y"
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutVector.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
visible: vecSize > 2
|
||||
SpinBox {
|
||||
id: boxZ
|
||||
minimumValue: -9999999
|
||||
maximumValue: 9999999
|
||||
decimals: 2
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
text: "Z"
|
||||
tooltip: "Z"
|
||||
visible: vecSize > 2
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
SpinBox {
|
||||
id: boxW
|
||||
minimumValue: -9999999
|
||||
maximumValue: 9999999
|
||||
decimals: 2
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
visible: vecSize > 3
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
|
||||
|
||||
ControlLabel {
|
||||
text: "W"
|
||||
tooltip: "W"
|
||||
visible: vecSize > 3
|
||||
}
|
||||
|
||||
Spacer { implicitWidth: StudioTheme.Values.controlGap }
|
||||
|
||||
Item {
|
||||
height: 10
|
||||
implicitWidth: {
|
||||
return StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
visible: vecSize === 2 // Placeholder for last spinbox
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component readonlyEditor: Component {
|
||||
id: readonlyEditor
|
||||
SecondColumnLayout {
|
||||
id: layoutReadonly
|
||||
property var backendValue
|
||||
property string propertyType
|
||||
|
||||
signal remove
|
||||
|
||||
PropertyLabel {
|
||||
tooltip: layoutReadonly.propertyType
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
leftPadding: StudioTheme.Values.actionIndicatorWidth
|
||||
text: qsTr("No editor for type: ") + layoutReadonly.propertyType
|
||||
|
||||
width: StudioTheme.Values.singleControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
}
|
||||
|
||||
Spacer {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnGap
|
||||
}
|
||||
IconIndicator {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
icon: StudioTheme.Constants.closeCross
|
||||
onClicked: layoutReadonly.remove()
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: root.propertiesModel
|
||||
|
||||
property bool loadActive: true
|
||||
onCountChanged: {
|
||||
repeater.loadActive = false
|
||||
repeater.loadActive = true
|
||||
}
|
||||
|
||||
SectionLayout {
|
||||
DynamicPropertyRow {
|
||||
id: propertyRow
|
||||
model: root.propertiesModel
|
||||
row: index
|
||||
}
|
||||
PropertyLabel {
|
||||
text: propertyName
|
||||
tooltip: propertyType
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
asynchronous: true
|
||||
active: repeater.loadActive
|
||||
width: loader.item ? loader.item.width : 0
|
||||
height: loader.item ? loader.item.height : 0
|
||||
|
||||
sourceComponent: {
|
||||
if (propertyType == "color")
|
||||
return colorEditor
|
||||
if (propertyType == "int")
|
||||
return intEditor
|
||||
if (propertyType == "real")
|
||||
return realEditor
|
||||
if (propertyType == "string")
|
||||
return stringEditor
|
||||
if (propertyType == "bool")
|
||||
return boolEditor
|
||||
if (propertyType == "url")
|
||||
return urlEditor
|
||||
if (propertyType == "alias")
|
||||
return aliasEditor
|
||||
if (propertyType == "variant")
|
||||
return readonlyEditor
|
||||
if (propertyType == "TextureInput")
|
||||
return textureInputEditor
|
||||
if (propertyType == "vector2d" || propertyType == "vector3d" || propertyType == "vector4d")
|
||||
return vectorEditor
|
||||
|
||||
return readonlyEditor
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
loader.item.backendValue = propertyRow.backendValue
|
||||
loader.item.propertyType = propertyType
|
||||
if (sourceComponent == vectorEditor) {
|
||||
let vecSize = 2
|
||||
if (propertyType == "vector3d")
|
||||
vecSize = 3
|
||||
else if (propertyType == "vector4d")
|
||||
vecSize = 4
|
||||
propertyRow.clearProxyBackendValues()
|
||||
|
||||
for (let i = 0; i < vecSize; ++i) {
|
||||
var newProxyValue = propertyRow.createProxyBackendValue()
|
||||
loader.item.proxyValues.push(newProxyValue)
|
||||
newProxyValue.valueChangedQml.connect(loader.item.updateExpression)
|
||||
loader.item.spinBoxes[i].backendValue = newProxyValue
|
||||
}
|
||||
propertyRow.backendValue.expressionChanged.connect(loader.item.updateProxyValues)
|
||||
loader.item.vecSize = vecSize
|
||||
loader.item.updateProxyValues()
|
||||
} else if (sourceComponent == aliasEditor) {
|
||||
loader.item.lineEdit.text = propertyRow.backendValue.expression
|
||||
loader.item.backendValue.expressionChanged.connect(loader.item.updateLineEditText)
|
||||
} else if (sourceComponent == colorEditor) {
|
||||
loader.item.initEditor()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: loader.item
|
||||
function onRemove() {
|
||||
propertyRow.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionLayout {
|
||||
PropertyLabel {
|
||||
text: ""
|
||||
tooltip: ""
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
|
||||
id: plusButton
|
||||
buttonIcon: StudioTheme.Constants.plus
|
||||
onClicked: {
|
||||
cePopup.opened ? cePopup.close() : cePopup.open()
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property T.Popup popup: T.Popup {
|
||||
id: cePopup
|
||||
|
||||
onOpened: {
|
||||
cePopup.setPopupY()
|
||||
cePopup.setMainScrollViewHeight()
|
||||
}
|
||||
|
||||
function setMainScrollViewHeight() {
|
||||
if (Controller.mainScrollView == null)
|
||||
return
|
||||
|
||||
var mappedPos = plusButton.mapToItem(Controller.mainScrollView.contentItem,
|
||||
cePopup.x, cePopup.y)
|
||||
Controller.mainScrollView.temporaryHeight = mappedPos.y + cePopup.height
|
||||
+ StudioTheme.Values.colorEditorPopupMargin
|
||||
}
|
||||
|
||||
function setPopupY() {
|
||||
if (Controller.mainScrollView == null)
|
||||
return
|
||||
|
||||
var mappedPos = plusButton.mapToItem(Controller.mainScrollView.contentItem,
|
||||
plusButton.x, plusButton.y)
|
||||
cePopup.y = Math.max(-mappedPos.y + StudioTheme.Values.colorEditorPopupMargin,
|
||||
cePopup.__defaultY)
|
||||
|
||||
textField.text = root.propertiesModel.newPropertyName()
|
||||
}
|
||||
|
||||
onClosed: Controller.mainScrollView.temporaryHeight = 0
|
||||
|
||||
property real __defaultX: (Controller.mainScrollView.contentItem.width
|
||||
- StudioTheme.Values.colorEditorPopupWidth * 1.5) / 2
|
||||
|
||||
property real __defaultY: - StudioTheme.Values.colorEditorPopupPadding
|
||||
- (StudioTheme.Values.colorEditorPopupSpacing * 2)
|
||||
- StudioTheme.Values.defaultControlHeight
|
||||
- StudioTheme.Values.colorEditorPopupLineHeight
|
||||
+ plusButton.width * 0.5
|
||||
|
||||
x: cePopup.__defaultX
|
||||
y: cePopup.__defaultY
|
||||
|
||||
width: 270
|
||||
height: 160
|
||||
|
||||
property int itemWidth: width / 2
|
||||
property int labelWidth: itemWidth - 32
|
||||
|
||||
padding: StudioTheme.Values.border
|
||||
margins: 2 // If not defined margin will be -1
|
||||
|
||||
closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent
|
||||
|
||||
contentItem: Item {
|
||||
id: content
|
||||
Column {
|
||||
spacing: StudioTheme.Values.sectionRowSpacing
|
||||
RowLayout {
|
||||
width: cePopup.width - 8
|
||||
PropertyLabel {
|
||||
text: "Add New Property"
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
leftPadding: 8
|
||||
width: cePopup.width - closeIndicator.width - 24
|
||||
}
|
||||
IconIndicator {
|
||||
id: closeIndicator
|
||||
icon: StudioTheme.Constants.colorPopupClose
|
||||
pixelSize: StudioTheme.Values.myIconFontSize * 1.4
|
||||
onClicked: cePopup.close()
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
PropertyLabel {
|
||||
id: textLabel
|
||||
text: "Name"
|
||||
width: cePopup.labelWidth
|
||||
}
|
||||
StudioControls.TextField {
|
||||
id: textField
|
||||
actionIndicator.visible: false
|
||||
translationIndicatorVisible: false
|
||||
width: cePopup.itemWidth
|
||||
rightPadding: 8
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
PropertyLabel {
|
||||
text: "Type"
|
||||
width: cePopup.labelWidth
|
||||
}
|
||||
StudioControls.ComboBox {
|
||||
id: comboBox
|
||||
actionIndicator.visible: false
|
||||
model: ["int", "real", "color", "string", "bool", "url", "alias",
|
||||
"TextureInput", "vector2d", "vector3d", "vector4d"]
|
||||
width: cePopup.itemWidth
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: 1
|
||||
height: StudioTheme.Values.sectionRowSpacing
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
width: cePopup.width
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
id: acceptButton
|
||||
|
||||
buttonIcon: qsTr("Add Property")
|
||||
iconFont: StudioTheme.Constants.font
|
||||
width: cePopup.width / 3
|
||||
|
||||
onClicked: {
|
||||
root.propertiesModel.createProperty(textField.text, comboBox.currentText)
|
||||
cePopup.close()
|
||||
}
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeInteraction
|
||||
border.width: StudioTheme.Values.border
|
||||
MouseArea {
|
||||
// This area is to eat clicks so they do not go through the popup
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.AllButtons
|
||||
}
|
||||
}
|
||||
|
||||
enter: Transition {}
|
||||
exit: Transition {}
|
||||
}
|
||||
}
|
@@ -83,7 +83,7 @@ HelperWidgets.ComboBox {
|
||||
|
||||
comboBox.currentIndex = comboBox.find(text)
|
||||
|
||||
if (text === "") {
|
||||
if (text === "" || text === "null") {
|
||||
comboBox.currentIndex = 0
|
||||
comboBox.editText = comboBox.defaultItem
|
||||
} else {
|
||||
|
@@ -62,6 +62,10 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
onBackendValueChanged: {
|
||||
spinBox.enabled = backendValue === undefined ? false : !isBlocked(backendValue.name)
|
||||
}
|
||||
|
||||
StudioControls.RealSpinBox {
|
||||
id: spinBox
|
||||
|
||||
|
@@ -20,6 +20,7 @@ ComponentSection 2.0 ComponentSection.qml
|
||||
ControlLabel 2.0 ControlLabel.qml
|
||||
singleton Controller 2.0 Controller.qml
|
||||
DoubleSpinBox 2.0 DoubleSpinBox.qml
|
||||
DynamicPropertiesSection 2.0 DynamicPropertiesSection.qml
|
||||
EditableListView 2.0 EditableListView.qml
|
||||
ExpandingSpacer 2.0 ExpandingSpacer.qml
|
||||
ExpressionTextField 2.0 ExpressionTextField.qml
|
||||
|
@@ -301,6 +301,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
fileresourcesmodel.cpp fileresourcesmodel.h
|
||||
itemfiltermodel.cpp itemfiltermodel.h
|
||||
gradientmodel.cpp gradientmodel.h
|
||||
dynamicpropertiesproxymodel.cpp dynamicpropertiesproxymodel.h
|
||||
gradientpresetcustomlistmodel.cpp gradientpresetcustomlistmodel.h
|
||||
gradientpresetdefaultlistmodel.cpp gradientpresetdefaultlistmodel.h
|
||||
gradientpresetitem.cpp gradientpresetitem.h
|
||||
@@ -322,6 +323,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
SOURCES_PREFIX components/materialeditor
|
||||
SOURCES
|
||||
materialeditorcontextobject.cpp materialeditorcontextobject.h
|
||||
materialeditordynamicpropertiesproxymodel.cpp materialeditordynamicpropertiesproxymodel.h
|
||||
materialeditorqmlbackend.cpp materialeditorqmlbackend.h
|
||||
materialeditortransaction.cpp materialeditortransaction.h
|
||||
materialeditorview.cpp materialeditorview.h
|
||||
@@ -452,6 +454,7 @@ extend_qtc_plugin(QmlDesigner
|
||||
connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui
|
||||
delegates.cpp delegates.h
|
||||
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
|
||||
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
|
@@ -50,7 +50,7 @@ ConnectionView::ConnectionView(QObject *parent) : AbstractView(parent),
|
||||
m_connectionViewWidget(new ConnectionViewWidget()),
|
||||
m_connectionModel(new ConnectionModel(this)),
|
||||
m_bindingModel(new BindingModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)),
|
||||
m_backendModel(new BackendModel(this))
|
||||
{
|
||||
connectionViewWidget()->setBindingModel(m_bindingModel);
|
||||
@@ -65,7 +65,7 @@ void ConnectionView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->reset();
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
backendModel()->resetModel();
|
||||
@@ -75,7 +75,7 @@ void ConnectionView::modelAboutToBeDetached(Model *model)
|
||||
{
|
||||
AbstractView::modelAboutToBeDetached(model);
|
||||
bindingModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>());
|
||||
dynamicPropertiesModel()->reset();
|
||||
connectionModel()->resetModel();
|
||||
connectionViewWidget()->resetItemViews();
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &p
|
||||
bindingModel()->bindingRemoved(property.toBindingProperty());
|
||||
dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty());
|
||||
} else if (property.isVariantProperty()) {
|
||||
//### dynamicPropertiesModel->bindingRemoved(property.toVariantProperty());
|
||||
dynamicPropertiesModel()->variantRemoved(property.toVariantProperty());
|
||||
} else if (property.isSignalHandlerProperty()) {
|
||||
connectionModel()->removeRowFromTable(property.toSignalHandlerProperty());
|
||||
}
|
||||
@@ -167,7 +167,7 @@ void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeL
|
||||
const QList<ModelNode> & /*lastSelectedNodeList*/)
|
||||
{
|
||||
bindingModel()->selectionChanged(selectedNodeList);
|
||||
dynamicPropertiesModel()->selectionChanged(selectedNodeList);
|
||||
dynamicPropertiesModel()->reset();
|
||||
connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex());
|
||||
connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex());
|
||||
|
||||
|
@@ -374,8 +374,8 @@ void ConnectionViewWidget::invalidateButtonStatus()
|
||||
} else if (currentTab() == DynamicPropertiesTab) {
|
||||
emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection());
|
||||
auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
|
||||
emit setEnabledAddButton(dynamicPropertiesModel->connectionView()->model() &&
|
||||
dynamicPropertiesModel->connectionView()->selectedModelNodes().count() == 1);
|
||||
emit setEnabledAddButton(dynamicPropertiesModel->view()->model() &&
|
||||
dynamicPropertiesModel->selectedNodes().count() == 1);
|
||||
} else if (currentTab() == BackendTab) {
|
||||
emit setEnabledAddButton(true);
|
||||
emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection());
|
||||
@@ -545,9 +545,9 @@ void ConnectionViewWidget::editorForDynamic()
|
||||
QString newValue = m_dynamicEditor->bindingValue().trimmed();
|
||||
|
||||
if (m_dynamicIndex.isValid()) {
|
||||
if (propertiesModel->connectionView()->isWidgetEnabled()
|
||||
if (qobject_cast<ConnectionView *>(propertiesModel->view())->isWidgetEnabled()
|
||||
&& (propertiesModel->rowCount() > m_dynamicIndex.row())) {
|
||||
propertiesModel->connectionView()->executeInTransaction(
|
||||
propertiesModel->view()->executeInTransaction(
|
||||
"ConnectionView::setBinding", [this, propertiesModel, newValue]() {
|
||||
AbstractProperty abProp = propertiesModel->abstractPropertyForRow(
|
||||
m_dynamicIndex.row());
|
||||
|
@@ -209,11 +209,11 @@ QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOp
|
||||
return widget;
|
||||
}
|
||||
|
||||
if (!model->connectionView()) {
|
||||
if (!model->view()) {
|
||||
qWarning() << "BindingDelegate::createEditor no connection view";
|
||||
return widget;
|
||||
}
|
||||
model->connectionView()->allModelNodes();
|
||||
model->view()->allModelNodes();
|
||||
|
||||
switch (index.column()) {
|
||||
case DynamicPropertiesModel::TargetModelNodeRow: {
|
||||
@@ -239,6 +239,10 @@ QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOp
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("url"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("color"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("variant"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("TextureInput"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("vector2d"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("vector3d"));
|
||||
dynamicPropertiesComboBox->addItem(QLatin1String("vector4d"));
|
||||
return dynamicPropertiesComboBox;
|
||||
};
|
||||
case DynamicPropertiesModel::PropertyValueRow: {
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <qmldesignerconstants.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
@@ -90,6 +91,16 @@ QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::T
|
||||
} else {
|
||||
returnValue = QColor(Qt::black);
|
||||
}
|
||||
} else if (typeName == "vector2d") {
|
||||
returnValue = "Qt.vector2d(0, 0)";
|
||||
} else if (typeName == "vector3d") {
|
||||
returnValue = "Qt.vector3d(0, 0, 0)";
|
||||
} else if (typeName == "vector4d") {
|
||||
returnValue = "Qt.vector4d(0, 0, 0 ,0)";
|
||||
} else if (typeName == "TextureInput") {
|
||||
returnValue = "null";
|
||||
} else if (typeName == "alias") {
|
||||
returnValue = "null";
|
||||
} else if (typeName == "Item") {
|
||||
returnValue = 0;
|
||||
}
|
||||
@@ -119,9 +130,18 @@ QmlDesigner::PropertyName DynamicPropertiesModel::unusedProperty(const QmlDesign
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(ConnectionView *parent)
|
||||
bool DynamicPropertiesModel::isValueType(const TypeName &type)
|
||||
{
|
||||
// "variant" is considered value type as it is initialized as one.
|
||||
// This may need to change if we provide any kind of proper editor for it.
|
||||
static const QSet<TypeName> valueTypes {"int", "real", "color", "string", "bool", "url", "variant"};
|
||||
return valueTypes.contains(type);
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_connectionView(parent)
|
||||
, m_view(parent)
|
||||
, m_explicitSelection(explicitSelection)
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
|
||||
}
|
||||
@@ -133,8 +153,9 @@ void DynamicPropertiesModel::resetModel()
|
||||
setHorizontalHeaderLabels(
|
||||
QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}));
|
||||
|
||||
if (connectionView()->isAttached()) {
|
||||
for (const ModelNode &modelNode : connectionView()->selectedModelNodes())
|
||||
if (m_view->isAttached()) {
|
||||
const auto nodes = selectedNodes();
|
||||
for (const ModelNode &modelNode : nodes)
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
@@ -146,8 +167,8 @@ void DynamicPropertiesModel::resetModel()
|
||||
//Value copying is optional
|
||||
BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue)
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
if (modelNode.hasVariantProperty(name)) {
|
||||
try {
|
||||
@@ -181,8 +202,8 @@ BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const Property
|
||||
//If it's a BindingProperty, then replaces it with empty VariantProperty
|
||||
void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
{
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
if (modelNode.hasProperty(name)) {
|
||||
try {
|
||||
@@ -190,11 +211,10 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
|
||||
if (abProp.isVariantProperty()) {
|
||||
VariantProperty property = abProp.toVariantProperty();
|
||||
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), property.dynamicTypeName());
|
||||
QVariant newValue = convertVariantForTypeName({}, property.dynamicTypeName());
|
||||
property.setDynamicTypeNameAndValue(property.dynamicTypeName(),
|
||||
newValue);
|
||||
}
|
||||
else if (abProp.isBindingProperty()) {
|
||||
} else if (abProp.isBindingProperty()) {
|
||||
BindingProperty property = abProp.toBindingProperty();
|
||||
TypeName oldType = property.dynamicTypeName();
|
||||
|
||||
@@ -202,9 +222,8 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
|
||||
modelNode.removeProperty(name);
|
||||
|
||||
VariantProperty newProperty = modelNode.variantProperty(name);
|
||||
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), oldType);
|
||||
newProperty.setDynamicTypeNameAndValue(oldType,
|
||||
newValue);
|
||||
QVariant newValue = convertVariantForTypeName({}, oldType);
|
||||
newProperty.setDynamicTypeNameAndValue(oldType, newValue);
|
||||
}
|
||||
|
||||
} catch (RewritingException &e) {
|
||||
@@ -226,8 +245,8 @@ void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindi
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
@@ -249,17 +268,16 @@ void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &varia
|
||||
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(variantProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(variantProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForVariantProperty(variantProperty);
|
||||
|
||||
if (rowNumber == -1) {
|
||||
if (rowNumber == -1)
|
||||
addVariantProperty(variantProperty);
|
||||
} else {
|
||||
else
|
||||
updateVariantProperty(rowNumber);
|
||||
}
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
@@ -269,8 +287,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
|
||||
if (!selectedNodes.contains(bindingProperty.parentModelNode()))
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(bindingProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForBindingProperty(bindingProperty);
|
||||
@@ -280,17 +298,36 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::selectionChanged(const QList<ModelNode> &selectedNodes)
|
||||
void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
|
||||
const QList<ModelNode> nodes = selectedNodes();
|
||||
if (!nodes.contains(variantProperty.parentModelNode()))
|
||||
return;
|
||||
if (!m_lock) {
|
||||
int rowNumber = findRowForVariantProperty(variantProperty);
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::reset()
|
||||
{
|
||||
Q_UNUSED(selectedNodes)
|
||||
m_handleDataChanged = false;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
ConnectionView *DynamicPropertiesModel::connectionView() const
|
||||
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
|
||||
{
|
||||
return m_connectionView;
|
||||
QTC_ASSERT(m_explicitSelection, return);
|
||||
QTC_ASSERT(node.isValid(), return);
|
||||
|
||||
m_selectedNodes.clear();
|
||||
m_selectedNodes.append(node);
|
||||
reset();
|
||||
}
|
||||
|
||||
AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const
|
||||
@@ -298,10 +335,10 @@ AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) c
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
if (!connectionView()->isAttached())
|
||||
if (!m_view->isAttached())
|
||||
return AbstractProperty();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.property(targetPropertyName.toUtf8());
|
||||
@@ -314,7 +351,7 @@ BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) con
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.bindingProperty(targetPropertyName.toUtf8());
|
||||
@@ -327,7 +364,7 @@ VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) con
|
||||
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
|
||||
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
|
||||
|
||||
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
|
||||
ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
|
||||
|
||||
if (modelNode.isValid())
|
||||
return modelNode.variantProperty(targetPropertyName.toUtf8());
|
||||
@@ -364,8 +401,8 @@ void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
|
||||
{
|
||||
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED);
|
||||
|
||||
if (connectionView()->selectedModelNodes().count() == 1) {
|
||||
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
|
||||
if (selectedNodes().count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes().constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
try {
|
||||
modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
|
||||
@@ -420,16 +457,14 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
|
||||
|
||||
void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
|
||||
{
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
if (bindingProperty.isValid()) {
|
||||
bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
} else {
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
if (variantProperty.isValid())
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -455,7 +490,6 @@ void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
|
||||
items.append(idItem);
|
||||
items.append(propertyNameItem);
|
||||
|
||||
|
||||
propertyTypeItem = new QStandardItem(propertyType);
|
||||
items.append(propertyTypeItem);
|
||||
|
||||
@@ -531,7 +565,7 @@ void DynamicPropertiesModel::updateValue(int row)
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = data(index(row, PropertyValueRow)).toString();
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
|
||||
transaction.commit(); //committing in the try block
|
||||
@@ -547,7 +581,7 @@ void DynamicPropertiesModel::updateValue(int row)
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = data(index(row, PropertyValueRow));
|
||||
|
||||
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
|
||||
try {
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
|
||||
transaction.commit(); //committing in the try block
|
||||
@@ -571,7 +605,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
|
||||
@@ -590,7 +624,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
|
||||
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
|
||||
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
});
|
||||
@@ -616,7 +650,7 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
const PropertyName propertyName = bindingProperty.name();
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
|
||||
});
|
||||
@@ -632,12 +666,14 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
const PropertyName propertyName = variantProperty.name();
|
||||
|
||||
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
if (newType == "alias") { //alias properties have to be bindings
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none"));
|
||||
if (!isValueType(newType)) {
|
||||
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(
|
||||
newType, convertVariantForTypeName({}, newType).toString());
|
||||
} else {
|
||||
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType));
|
||||
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(
|
||||
newType, convertVariantForTypeName(value, newType));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -656,7 +692,7 @@ ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const M
|
||||
ModelNode modelNode;
|
||||
|
||||
if (id != QLatin1String("parent")) {
|
||||
modelNode = connectionView()->modelNodeForId(id);
|
||||
modelNode = m_view->modelNodeForId(id);
|
||||
} else {
|
||||
if (targetNode.hasParentProperty()) {
|
||||
modelNode = targetNode.parentProperty().parentModelNode();
|
||||
@@ -776,6 +812,24 @@ void DynamicPropertiesModel::handleException()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
const QList<ModelNode> DynamicPropertiesModel::selectedNodes() const
|
||||
{
|
||||
// If selected nodes are explicitly set, return those.
|
||||
// Otherwise return actual selected nodes of the model.
|
||||
if (m_explicitSelection)
|
||||
return m_selectedNodes;
|
||||
else
|
||||
return m_view->selectedModelNodes();
|
||||
}
|
||||
|
||||
const ModelNode DynamicPropertiesModel::singleSelectedNode() const
|
||||
{
|
||||
if (m_explicitSelection)
|
||||
return m_selectedNodes.first();
|
||||
else
|
||||
return m_view->singleSelectedModelNode();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -33,9 +33,9 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace Internal {
|
||||
class AbstractView;
|
||||
|
||||
class ConnectionView;
|
||||
namespace Internal {
|
||||
|
||||
class DynamicPropertiesModel : public QStandardItemModel
|
||||
{
|
||||
@@ -48,13 +48,17 @@ public:
|
||||
PropertyTypeRow = 2,
|
||||
PropertyValueRow = 3
|
||||
};
|
||||
DynamicPropertiesModel(ConnectionView *parent = nullptr);
|
||||
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
void variantPropertyChanged(const VariantProperty &variantProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
void selectionChanged(const QList<ModelNode> &selectedNodes);
|
||||
void variantRemoved(const VariantProperty &variantProperty);
|
||||
void reset();
|
||||
void setSelectedNode(const ModelNode &node);
|
||||
const QList<ModelNode> selectedNodes() const;
|
||||
const ModelNode singleSelectedNode() const;
|
||||
|
||||
ConnectionView *connectionView() const;
|
||||
AbstractView *view() const { return m_view; }
|
||||
AbstractProperty abstractPropertyForRow(int rowNumber) const;
|
||||
BindingProperty bindingPropertyForRow(int rowNumber) const;
|
||||
VariantProperty variantPropertyForRow(int rowNumber) const;
|
||||
@@ -71,6 +75,8 @@ public:
|
||||
|
||||
QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode);
|
||||
|
||||
static bool isValueType(const TypeName &type);
|
||||
|
||||
protected:
|
||||
void addProperty(const QVariant &propertyValue,
|
||||
const QString &propertyType,
|
||||
@@ -97,12 +103,12 @@ private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
||||
private:
|
||||
ConnectionView *m_connectionView;
|
||||
AbstractView *m_view = nullptr;
|
||||
bool m_lock = false;
|
||||
bool m_handleDataChanged = false;
|
||||
QString m_exceptionError;
|
||||
|
||||
QList<ModelNode> m_selectedNodes;
|
||||
bool m_explicitSelection = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "selectiondynamicpropertiesproxymodel.h"
|
||||
|
||||
#include <dynamicpropertiesmodel.h>
|
||||
#include <connectionview.h>
|
||||
|
||||
using namespace QmlDesigner::Internal;
|
||||
|
||||
SelectionDynamicPropertiesProxyModel::SelectionDynamicPropertiesProxyModel(QObject *parent)
|
||||
: DynamicPropertiesProxyModel(parent)
|
||||
{
|
||||
initModel(ConnectionView::instance()->dynamicPropertiesModel());
|
||||
}
|
||||
|
||||
void SelectionDynamicPropertiesProxyModel::registerDeclarativeType()
|
||||
{
|
||||
DynamicPropertiesProxyModel::registerDeclarativeType();
|
||||
qmlRegisterType<SelectionDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "SelectionDynamicPropertiesModel");
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dynamicpropertiesproxymodel.h>
|
||||
|
||||
class SelectionDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelectionDynamicPropertiesProxyModel(QObject *parent = nullptr);
|
||||
|
||||
static void registerDeclarativeType();
|
||||
};
|
@@ -0,0 +1,44 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "materialeditordynamicpropertiesproxymodel.h"
|
||||
|
||||
#include <dynamicpropertiesmodel.h>
|
||||
|
||||
#include <materialeditorview.h>
|
||||
|
||||
using namespace QmlDesigner;
|
||||
|
||||
MaterialEditorDynamicPropertiesProxyModel::MaterialEditorDynamicPropertiesProxyModel(QObject *parent)
|
||||
: DynamicPropertiesProxyModel(parent)
|
||||
{
|
||||
initModel(MaterialEditorView::instance()->dynamicPropertiesModel());
|
||||
}
|
||||
|
||||
void MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType()
|
||||
{
|
||||
DynamicPropertiesProxyModel::registerDeclarativeType();
|
||||
qmlRegisterType<MaterialEditorDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "MaterialEditorDynamicPropertiesModel");
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dynamicpropertiesproxymodel.h"
|
||||
|
||||
class MaterialEditorDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MaterialEditorDynamicPropertiesProxyModel(QObject *parent = nullptr);
|
||||
|
||||
static void registerDeclarativeType();
|
||||
};
|
@@ -27,11 +27,13 @@
|
||||
|
||||
#include "materialeditorqmlbackend.h"
|
||||
#include "materialeditorcontextobject.h"
|
||||
#include "materialeditordynamicpropertiesproxymodel.h"
|
||||
#include "propertyeditorvalue.h"
|
||||
#include "materialeditortransaction.h"
|
||||
#include "assetslibrarywidget.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <dynamicpropertiesmodel.h>
|
||||
#include <metainfo.h>
|
||||
#include <nodeinstanceview.h>
|
||||
#include <nodelistproperty.h>
|
||||
@@ -70,6 +72,7 @@ namespace QmlDesigner {
|
||||
MaterialEditorView::MaterialEditorView(QWidget *parent)
|
||||
: AbstractView(parent)
|
||||
, m_stackedWidget(new QStackedWidget(parent))
|
||||
, m_dynamicPropertiesModel(new Internal::DynamicPropertiesModel(true, this))
|
||||
{
|
||||
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7), m_stackedWidget);
|
||||
connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml);
|
||||
@@ -92,6 +95,8 @@ MaterialEditorView::MaterialEditorView(QWidget *parent)
|
||||
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||
m_stackedWidget->setMinimumWidth(250);
|
||||
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME);
|
||||
|
||||
MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType();
|
||||
}
|
||||
|
||||
MaterialEditorView::~MaterialEditorView()
|
||||
@@ -314,6 +319,29 @@ void MaterialEditorView::currentTimelineChanged(const ModelNode &)
|
||||
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
|
||||
}
|
||||
|
||||
Internal::DynamicPropertiesModel *MaterialEditorView::dynamicPropertiesModel() const
|
||||
{
|
||||
return m_dynamicPropertiesModel;
|
||||
}
|
||||
|
||||
MaterialEditorView *MaterialEditorView::instance()
|
||||
{
|
||||
static MaterialEditorView *s_instance = nullptr;
|
||||
|
||||
if (s_instance)
|
||||
return s_instance;
|
||||
|
||||
const auto views = QmlDesignerPlugin::instance()->viewManager().views();
|
||||
for (auto *view : views) {
|
||||
MaterialEditorView *myView = qobject_cast<MaterialEditorView *>(view);
|
||||
if (myView)
|
||||
s_instance = myView;
|
||||
}
|
||||
|
||||
QTC_ASSERT(s_instance, return nullptr);
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void MaterialEditorView::delayedResetView()
|
||||
{
|
||||
// TODO: it seems the delayed reset is not needed. Leaving it commented out for now just in case it
|
||||
@@ -592,6 +620,11 @@ void MaterialEditorView::setupQmlBackend()
|
||||
|
||||
m_qmlBackEnd = currentQmlBackend;
|
||||
|
||||
if (m_hasMaterialRoot)
|
||||
m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial);
|
||||
else
|
||||
m_dynamicPropertiesModel->reset();
|
||||
|
||||
delayedTypeUpdate();
|
||||
initPreviewData();
|
||||
|
||||
@@ -751,6 +784,7 @@ void MaterialEditorView::modelAttached(Model *model)
|
||||
void MaterialEditorView::modelAboutToBeDetached(Model *model)
|
||||
{
|
||||
AbstractView::modelAboutToBeDetached(model);
|
||||
m_dynamicPropertiesModel->reset();
|
||||
m_qmlBackEnd->materialEditorTransaction()->end();
|
||||
}
|
||||
|
||||
@@ -783,8 +817,9 @@ void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &
|
||||
bool changed = false;
|
||||
for (const VariantProperty &property : propertyList) {
|
||||
ModelNode node(property.parentModelNode());
|
||||
|
||||
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->variantPropertyChanged(property);
|
||||
if (m_selectedMaterial.property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||
else
|
||||
@@ -810,6 +845,8 @@ void MaterialEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
|
||||
m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedMaterial).isAliasExported());
|
||||
|
||||
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
|
||||
if (property.isDynamic())
|
||||
m_dynamicPropertiesModel->bindingPropertyChanged(property);
|
||||
if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty())
|
||||
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
|
||||
else
|
||||
@@ -831,6 +868,16 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node, const Prope
|
||||
m_qmlBackEnd->setValueforAuxiliaryProperties(m_selectedMaterial, name);
|
||||
}
|
||||
|
||||
void MaterialEditorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if (property.isBindingProperty())
|
||||
m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty());
|
||||
else if (property.isVariantProperty())
|
||||
m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty());
|
||||
}
|
||||
}
|
||||
|
||||
// request render image for the selected material node
|
||||
void MaterialEditorView::requestPreviewRender()
|
||||
{
|
||||
@@ -998,6 +1045,7 @@ void MaterialEditorView::customNotification(const AbstractView *view, const QStr
|
||||
if (identifier == "selected_material_changed") {
|
||||
if (!m_hasMaterialRoot) {
|
||||
m_selectedMaterial = nodeList.first();
|
||||
m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial);
|
||||
QTimer::singleShot(0, this, &MaterialEditorView::resetView);
|
||||
}
|
||||
} else if (identifier == "apply_to_selected_triggered") {
|
||||
|
@@ -44,6 +44,10 @@ namespace QmlDesigner {
|
||||
class ModelNode;
|
||||
class MaterialEditorQmlBackend;
|
||||
|
||||
namespace Internal {
|
||||
class DynamicPropertiesModel;
|
||||
}
|
||||
|
||||
class MaterialEditorView : public AbstractView
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -66,6 +70,7 @@ public:
|
||||
void variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags propertyChange) override;
|
||||
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags propertyChange) override;
|
||||
void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override;
|
||||
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
|
||||
|
||||
void resetView();
|
||||
void currentStateChanged(const ModelNode &node) override;
|
||||
@@ -90,6 +95,10 @@ public:
|
||||
|
||||
void currentTimelineChanged(const ModelNode &node) override;
|
||||
|
||||
Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
|
||||
|
||||
static MaterialEditorView *instance();
|
||||
|
||||
public slots:
|
||||
void handleToolBarAction(int action);
|
||||
void handlePreviewEnvChanged(const QString &envAndValue);
|
||||
@@ -142,6 +151,7 @@ private:
|
||||
|
||||
QPointer<QColorDialog> m_colorDialog;
|
||||
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
|
||||
Internal::DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -0,0 +1,364 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dynamicpropertiesproxymodel.h"
|
||||
|
||||
#include "propertyeditorvalue.h"
|
||||
|
||||
#include <dynamicpropertiesmodel.h>
|
||||
|
||||
#include <abstractproperty.h>
|
||||
#include <bindingeditor.h>
|
||||
#include <variantproperty.h>
|
||||
#include <qmldesignerconstants.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace QmlDesigner;
|
||||
|
||||
static const int propertyNameRole = Qt::UserRole + 1;
|
||||
static const int propertyTypeRole = Qt::UserRole + 2;
|
||||
static const int propertyValueRole = Qt::UserRole + 3;
|
||||
static const int propertyBindingRole = Qt::UserRole + 4;
|
||||
|
||||
DynamicPropertiesProxyModel::DynamicPropertiesProxyModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void DynamicPropertiesProxyModel::initModel(QmlDesigner::Internal::DynamicPropertiesModel *model)
|
||||
{
|
||||
m_model = model;
|
||||
|
||||
connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
|
||||
this, &QAbstractItemModel::modelAboutToBeReset);
|
||||
connect(m_model, &QAbstractItemModel::modelReset,
|
||||
this, &QAbstractItemModel::modelReset);
|
||||
|
||||
connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
|
||||
this, &QAbstractItemModel::rowsAboutToBeRemoved);
|
||||
connect(m_model, &QAbstractItemModel::rowsRemoved,
|
||||
this, &QAbstractItemModel::rowsRemoved);
|
||||
connect(m_model, &QAbstractItemModel::rowsInserted,
|
||||
this, &QAbstractItemModel::rowsInserted);
|
||||
|
||||
connect(m_model, &QAbstractItemModel::dataChanged,
|
||||
this, [this](const QModelIndex &topLeft, const QModelIndex &, const QList<int> &) {
|
||||
emit dataChanged(index(topLeft.row(), 0),
|
||||
index(topLeft.row(), 0),
|
||||
{ propertyNameRole, propertyTypeRole,
|
||||
propertyValueRole, propertyBindingRole });
|
||||
});
|
||||
}
|
||||
|
||||
int DynamicPropertiesProxyModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return m_model->rowCount();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> DynamicPropertiesProxyModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{propertyNameRole, "propertyName"},
|
||||
{propertyTypeRole, "propertyType"},
|
||||
{propertyValueRole, "propertyValue"},
|
||||
{propertyBindingRole, "propertyBinding"}};
|
||||
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.isValid() && index.row() < rowCount()) {
|
||||
AbstractProperty property = m_model->abstractPropertyForRow(index.row());
|
||||
|
||||
QTC_ASSERT(property.isValid(), return QVariant());
|
||||
|
||||
if (role == propertyNameRole) {
|
||||
return property.name();
|
||||
} else if (propertyTypeRole) {
|
||||
return property.dynamicTypeName();
|
||||
} else if (role == propertyValueRole) {
|
||||
QmlObjectNode objectNode = property.parentQmlObjectNode();
|
||||
return objectNode.modelValue(property.name());
|
||||
} else if (role == propertyBindingRole) {
|
||||
if (property.isBindingProperty())
|
||||
return property.toBindingProperty().expression();
|
||||
return QVariant();
|
||||
}
|
||||
qWarning() << Q_FUNC_INFO << "invalid role";
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "invalid index";
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void DynamicPropertiesProxyModel::registerDeclarativeType()
|
||||
{
|
||||
static bool registered = false;
|
||||
if (!registered)
|
||||
qmlRegisterType<DynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "DynamicPropertiesModel");
|
||||
}
|
||||
|
||||
QmlDesigner::Internal::DynamicPropertiesModel *DynamicPropertiesProxyModel::dynamicPropertiesModel() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
QString DynamicPropertiesProxyModel::newPropertyName() const
|
||||
{
|
||||
auto propertiesModel = dynamicPropertiesModel();
|
||||
|
||||
return QString::fromUtf8(propertiesModel->unusedProperty(
|
||||
propertiesModel->singleSelectedNode()));
|
||||
}
|
||||
|
||||
void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type)
|
||||
{
|
||||
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED);
|
||||
|
||||
const auto selectedNodes = dynamicPropertiesModel()->selectedNodes();
|
||||
if (selectedNodes.count() == 1) {
|
||||
const ModelNode modelNode = selectedNodes.constFirst();
|
||||
if (modelNode.isValid()) {
|
||||
try {
|
||||
if (Internal::DynamicPropertiesModel::isValueType(type.toUtf8())) {
|
||||
QVariant value;
|
||||
if (type == "int")
|
||||
value = 0;
|
||||
else if (type == "real")
|
||||
value = 0.0;
|
||||
else if (type == "color")
|
||||
value = QColor(255, 255, 255);
|
||||
else if (type == "string")
|
||||
value = "";
|
||||
else if (type == "bool")
|
||||
value = false;
|
||||
else if (type == "url")
|
||||
value = "";
|
||||
else if (type == "variant")
|
||||
value = "";
|
||||
|
||||
modelNode.variantProperty(name.toUtf8())
|
||||
.setDynamicTypeNameAndValue(type.toUtf8(), value);
|
||||
} else {
|
||||
QString expression;
|
||||
if (type == "alias")
|
||||
expression = "null";
|
||||
else if (type == "TextureInput")
|
||||
expression = "null";
|
||||
else if (type == "vector2d")
|
||||
expression = "Qt.vector2d(0, 0)";
|
||||
else if (type == "vector3d")
|
||||
expression = "Qt.vector3d(0, 0, 0)";
|
||||
else if (type == "vector4d")
|
||||
expression = "Qt.vector4d(0, 0, 0 ,0)";
|
||||
|
||||
modelNode.bindingProperty(name.toUtf8())
|
||||
.setDynamicTypeNameAndExpression(type.toUtf8(), expression);
|
||||
}
|
||||
} catch (Exception &e) {
|
||||
e.showException();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
|
||||
}
|
||||
}
|
||||
|
||||
DynamicPropertyRow::DynamicPropertyRow(QObject *parent)
|
||||
{
|
||||
m_backendValue = new PropertyEditorValue(this);
|
||||
|
||||
QObject::connect(m_backendValue,
|
||||
&PropertyEditorValue::valueChanged,
|
||||
this,
|
||||
[this](const QString &, const QVariant &value) { commitValue(value); });
|
||||
|
||||
QObject::connect(m_backendValue,
|
||||
&PropertyEditorValue::expressionChanged,
|
||||
this,
|
||||
[this](const QString &) { commitExpression(m_backendValue->expression()); });
|
||||
}
|
||||
|
||||
DynamicPropertyRow::~DynamicPropertyRow()
|
||||
{
|
||||
clearProxyBackendValues();
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::registerDeclarativeType()
|
||||
{
|
||||
qmlRegisterType<DynamicPropertyRow>("HelperWidgets", 2, 0, "DynamicPropertyRow");
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::setRow(int r)
|
||||
{
|
||||
if (m_row == r)
|
||||
return;
|
||||
|
||||
m_row = r;
|
||||
setupBackendValue();
|
||||
emit rowChanged();
|
||||
}
|
||||
|
||||
int DynamicPropertyRow::row() const
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::setModel(DynamicPropertiesProxyModel *model)
|
||||
{
|
||||
if (model == m_model)
|
||||
return;
|
||||
|
||||
if (m_model) {
|
||||
disconnect(m_model, &QAbstractItemModel::dataChanged,
|
||||
this, &DynamicPropertyRow::handleDataChanged);
|
||||
}
|
||||
|
||||
m_model = model;
|
||||
|
||||
if (m_model) {
|
||||
connect(m_model, &QAbstractItemModel::dataChanged,
|
||||
this, &DynamicPropertyRow::handleDataChanged);
|
||||
|
||||
if (m_row != -1)
|
||||
setupBackendValue();
|
||||
}
|
||||
|
||||
emit modelChanged();
|
||||
}
|
||||
|
||||
DynamicPropertiesProxyModel *DynamicPropertyRow::model() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
PropertyEditorValue *DynamicPropertyRow::backendValue() const
|
||||
{
|
||||
return m_backendValue;
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::remove()
|
||||
{
|
||||
m_model->dynamicPropertiesModel()->deleteDynamicPropertyByRow(m_row);
|
||||
}
|
||||
|
||||
PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue()
|
||||
{
|
||||
|
||||
PropertyEditorValue *newValue = new PropertyEditorValue(this);
|
||||
m_proxyBackendValues.append(newValue);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::clearProxyBackendValues()
|
||||
{
|
||||
qDeleteAll(m_proxyBackendValues);
|
||||
m_proxyBackendValues.clear();
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::setupBackendValue()
|
||||
{
|
||||
if (!m_model)
|
||||
return;
|
||||
|
||||
QmlDesigner::AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row);
|
||||
if (!property.isValid())
|
||||
return;
|
||||
|
||||
if (m_backendValue->name() != property.name())
|
||||
m_backendValue->setName(property.name());
|
||||
|
||||
ModelNode node = property.parentModelNode();
|
||||
if (node != m_backendValue->modelNode())
|
||||
m_backendValue->setModelNode(node);
|
||||
|
||||
QVariant modelValue = property.parentQmlObjectNode().modelValue(property.name());
|
||||
if (modelValue != m_backendValue->value()) {
|
||||
m_backendValue->setValue({});
|
||||
m_backendValue->setValue(modelValue);
|
||||
}
|
||||
|
||||
if (property.isBindingProperty()) {
|
||||
QString expression = property.toBindingProperty().expression();
|
||||
if (m_backendValue->expression() != expression)
|
||||
m_backendValue->setExpression(expression);
|
||||
}
|
||||
|
||||
emit m_backendValue->isBoundChanged();
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::commitValue(const QVariant &value)
|
||||
{
|
||||
auto propertiesModel = m_model->dynamicPropertiesModel();
|
||||
VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row);
|
||||
|
||||
if (!Internal::DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName()))
|
||||
return;
|
||||
|
||||
auto view = propertiesModel->view();
|
||||
RewriterTransaction transaction = view->beginRewriterTransaction(
|
||||
QByteArrayLiteral("DynamicPropertiesModel::commitValue"));
|
||||
try {
|
||||
if (variantProperty.value() != value)
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) {
|
||||
e.showException();
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::commitExpression(const QString &expression)
|
||||
{
|
||||
auto propertiesModel = m_model->dynamicPropertiesModel();
|
||||
BindingProperty bindingProperty = propertiesModel->bindingPropertyForRow(m_row);
|
||||
|
||||
auto view = propertiesModel->view();
|
||||
RewriterTransaction transaction = view->beginRewriterTransaction(
|
||||
QByteArrayLiteral("DynamicPropertiesModel::commitExpression"));
|
||||
try {
|
||||
QString theExpression = expression;
|
||||
if (theExpression.isEmpty())
|
||||
theExpression = "null";
|
||||
|
||||
if (bindingProperty.expression() != theExpression) {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(),
|
||||
theExpression);
|
||||
}
|
||||
transaction.commit(); //committing in the try block
|
||||
} catch (Exception &e) {
|
||||
e.showException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void DynamicPropertyRow::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &, const QList<int> &)
|
||||
{
|
||||
if (topLeft.row() == m_row)
|
||||
setupBackendValue();
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "propertyeditorvalue.h"
|
||||
|
||||
#include <abstractview.h>
|
||||
#include <qmlitemnode.h>
|
||||
|
||||
#include <enumeration.h>
|
||||
#include <QAbstractListModel>
|
||||
#include <QColor>
|
||||
#include <QtQml>
|
||||
|
||||
namespace QmlDesigner {
|
||||
namespace Internal {
|
||||
class DynamicPropertiesModel;
|
||||
}
|
||||
} // namespace QmlDesigner
|
||||
|
||||
class DynamicPropertiesProxyModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DynamicPropertiesProxyModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
static void registerDeclarativeType();
|
||||
|
||||
QmlDesigner::Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
|
||||
|
||||
Q_INVOKABLE QString newPropertyName() const;
|
||||
Q_INVOKABLE void createProperty(const QString &name, const QString &type);
|
||||
|
||||
protected:
|
||||
void initModel(QmlDesigner::Internal::DynamicPropertiesModel *model);
|
||||
|
||||
private:
|
||||
QmlDesigner::Internal::DynamicPropertiesModel *m_model = nullptr;
|
||||
};
|
||||
|
||||
class DynamicPropertyRow : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL)
|
||||
Q_PROPERTY(PropertyEditorValue *backendValue READ backendValue NOTIFY rowChanged FINAL)
|
||||
Q_PROPERTY(DynamicPropertiesProxyModel *model READ model WRITE setModel NOTIFY modelChanged FINAL)
|
||||
|
||||
public:
|
||||
explicit DynamicPropertyRow(QObject *parent = nullptr);
|
||||
~DynamicPropertyRow();
|
||||
|
||||
static void registerDeclarativeType();
|
||||
|
||||
void setRow(int r);
|
||||
int row() const;
|
||||
void setModel(DynamicPropertiesProxyModel *model);
|
||||
DynamicPropertiesProxyModel *model() const;
|
||||
PropertyEditorValue *backendValue() const;
|
||||
|
||||
Q_INVOKABLE void remove();
|
||||
Q_INVOKABLE PropertyEditorValue *createProxyBackendValue();
|
||||
Q_INVOKABLE void clearProxyBackendValues();
|
||||
|
||||
signals:
|
||||
void rowChanged();
|
||||
void modelChanged();
|
||||
|
||||
private:
|
||||
void setupBackendValue();
|
||||
void commitValue(const QVariant &value);
|
||||
void commitExpression(const QString &expression);
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &, const QList<int> &);
|
||||
|
||||
int m_row = -1;
|
||||
PropertyEditorValue *m_backendValue = nullptr;
|
||||
DynamicPropertiesProxyModel *m_model = nullptr;
|
||||
QList<PropertyEditorValue *> m_proxyBackendValues;
|
||||
};
|
||||
|
||||
QML_DECLARE_TYPE(DynamicPropertyRow)
|
@@ -30,6 +30,7 @@
|
||||
#include "bindingeditor/actioneditor.h"
|
||||
#include "bindingeditor/bindingeditor.h"
|
||||
#include "colorpalettebackend.h"
|
||||
#include "selectiondynamicpropertiesproxymodel.h"
|
||||
#include "fileresourcesmodel.h"
|
||||
#include "gradientmodel.h"
|
||||
#include "gradientpresetcustomlistmodel.h"
|
||||
@@ -75,6 +76,8 @@ void Quick2PropertyEditorView::registerQmlTypes()
|
||||
Tooltip::registerDeclarativeType();
|
||||
EasingCurveEditor::registerDeclarativeType();
|
||||
RichTextEditorProxy::registerDeclarativeType();
|
||||
SelectionDynamicPropertiesProxyModel::registerDeclarativeType();
|
||||
DynamicPropertyRow::registerDeclarativeType();
|
||||
|
||||
const QString resourcePath = PropertyEditorQmlBackend::propertyEditorResourcesPath();
|
||||
|
||||
|
@@ -321,9 +321,10 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName,
|
||||
Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name());
|
||||
if (internalProperty->isBindingProperty()
|
||||
&& internalProperty->toBindingProperty()->expression() == expression
|
||||
&& internalProperty->toBindingProperty()->dynamicTypeName() == typeName)
|
||||
&& internalProperty->toBindingProperty()->dynamicTypeName() == typeName) {
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())
|
||||
|
@@ -691,6 +691,8 @@ Project {
|
||||
"materialbrowser/materialbrowserwidget.h",
|
||||
"materialeditor/materialeditorcontextobject.cpp",
|
||||
"materialeditor/materialeditorcontextobject.h",
|
||||
"materialeditor/materialeditordynamicpropertiesproxymodel.cpp",
|
||||
"materialeditor/materialeditordynamicpropertiesproxymodel.h",
|
||||
"materialeditor/materialeditorqmlbackend.cpp",
|
||||
"materialeditor/materialeditorqmlbackend.h",
|
||||
"materialeditor/materialeditortransaction.cpp",
|
||||
@@ -725,6 +727,8 @@ Project {
|
||||
"propertyeditor/colorpalettebackend.h",
|
||||
"propertyeditor/designerpropertymap.cpp",
|
||||
"propertyeditor/designerpropertymap.h",
|
||||
"propertyeditor/dynamicpropertiesproxymodel.cpp",
|
||||
"propertyeditor/dynamicpropertiesproxymodel.h",
|
||||
"propertyeditor/fileresourcesmodel.cpp",
|
||||
"propertyeditor/fileresourcesmodel.h",
|
||||
"propertyeditor/itemfiltermodel.cpp",
|
||||
@@ -843,6 +847,8 @@ Project {
|
||||
"connectioneditor/connectionviewwidget.ui",
|
||||
"connectioneditor/dynamicpropertiesmodel.cpp",
|
||||
"connectioneditor/dynamicpropertiesmodel.h",
|
||||
"connectioneditor/selectiondynamicpropertiesproxymodel.cpp",
|
||||
"connectioneditor/selectiondynamicpropertiesproxymodel.h",
|
||||
"connectioneditor/stylesheet.css",
|
||||
"curveeditor/curveeditorview.cpp",
|
||||
"curveeditor/curveeditorview.h",
|
||||
|
Reference in New Issue
Block a user