Merge remote-tracking branch 'origin/8.0'

Conflicts:
	.github/workflows/build_cmake.yml
	src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
	src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
	src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
	src/plugins/qmldesigner/designercore/model/model.cpp

Change-Id: I111b9140375b894a5487cc012b17cc32100bdb8d
This commit is contained in:
Eike Ziller
2022-09-13 11:15:16 +02:00
73 changed files with 2547 additions and 212 deletions

View File

@@ -7,7 +7,7 @@ on:
- 'doc/**' - 'doc/**'
env: env:
QT_VERSION: 6.3.1 QT_VERSION: 6.3.2
CLANG_VERSION: 15.0.0 CLANG_VERSION: 15.0.0
ELFUTILS_VERSION: 0.175 ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1 CMAKE_VERSION: 3.21.1

View File

@@ -13,7 +13,7 @@ instructions:
instructions: instructions:
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: QTC_QT_BASE_URL variableName: QTC_QT_BASE_URL
variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.3/6.3.1-final-released/Qt6.3.1" variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.3/6.3.2-final-released/Qt"
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: QTC_QT_MODULES variableName: QTC_QT_MODULES
variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine" variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine"

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -264,6 +264,28 @@
selected in the \uicontrol{3D} view. selected in the \uicontrol{3D} view.
\endtable \endtable
\section1 Changing Colors
To change the \uicontrol 3D view background or grid color, select
\inlineimage icons/3d-background-color.png
in the toolbar. This opens a menu with the following options:
\table
\row
\li Select Background Color
\li Select a color for the background.
\row
\li Select Grid Color
\li Select a color for the grid.
\row
\li Use Scene Environment Color
\li Sets the 3D view to use the scene environment color as background
color.
\row
\li Reset Colors
\li Resets the background and grid colors to the default colors.
\endtable
\section1 Particle Editor \section1 Particle Editor
The particle editor tools help you preview your particle systems in The particle editor tools help you preview your particle systems in
@@ -359,6 +381,11 @@
\li Visibility Toggles \li Visibility Toggles
\li \li
\li \l{Toggling Visibility} \li \l{Toggling Visibility}
\row
\li \inlineimage icons/3d-background-color.png
\li Background Color Actions
\li
\li \l{Changing Colors}
\row \row
\li \inlineimage icons/particles-seek.png \li \inlineimage icons/particles-seek.png
\li Seek Particle System Time \li Seek Particle System Time

View File

@@ -18,7 +18,7 @@ public:
ActiveSceneChanged, ActiveSceneChanged,
RenderModelNodePreviewImage, RenderModelNodePreviewImage,
Import3DSupport, Import3DSupport,
ModelAtPos, NodeAtPos,
None }; None };
PuppetToCreatorCommand(Type type, const QVariant &data); PuppetToCreatorCommand(Type type, const QVariant &data);

View File

@@ -38,7 +38,7 @@ public:
SelectGridColor, SelectGridColor,
ResetBackgroundColor, ResetBackgroundColor,
SyncBackgroundColor, SyncBackgroundColor,
GetModelAtPos GetNodeAtPos
}; };
View3DActionCommand(Type type, const QVariant &value); View3DActionCommand(Type type, const QVariant &value);

View File

@@ -525,6 +525,19 @@ Item {
} }
} }
function gizmoAt(x, y)
{
for (var i = 0; i < lightIconGizmos.length; ++i) {
if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y))
return lightIconGizmos[i].targetNode;
}
for (var i = 0; i < cameraGizmos.length; ++i) {
if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y))
return cameraGizmos[i].targetNode;
}
return null;
}
Component.onCompleted: { Component.onCompleted: {
createEditView(); createEditView();
selectObjects([]); selectObjects([]);

View File

@@ -30,6 +30,17 @@ Item {
signal clicked(Node node, bool multi) signal clicked(Node node, bool multi)
function hasPoint(x, y)
{
if (!view3D || !targetNode)
return false;
var point = view3D.mapToItem(iconMouseArea, x, y);
return point.x >= iconMouseArea.x && (point.x <= iconMouseArea.x + iconMouseArea.width)
&& point.y >= iconMouseArea.y && (point.y <= iconMouseArea.y + iconMouseArea.height);
}
onSelectedChanged: { onSelectedChanged: {
if (selected) if (selected)
hasMouse = false; hasMouse = false;

View File

@@ -648,6 +648,23 @@ Item {
} }
} }
function gizmoAt(x, y)
{
for (var i = 0; i < lightIconGizmos.length; ++i) {
if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y))
return lightIconGizmos[i].targetNode;
}
for (var i = 0; i < cameraGizmos.length; ++i) {
if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y))
return cameraGizmos[i].targetNode;
}
for (var i = 0; i < particleSystemIconGizmos.length; ++i) {
if (particleSystemIconGizmos[i].visible && particleSystemIconGizmos[i].hasPoint(x, y))
return particleSystemIconGizmos[i].targetNode;
}
return null;
}
Component.onCompleted: { Component.onCompleted: {
createEditView(); createEditView();
selectObjects([]); selectObjects([]);

View File

@@ -31,6 +31,17 @@ Item {
signal clicked(Node node, bool multi) signal clicked(Node node, bool multi)
function hasPoint(x, y)
{
if (!view3D || !targetNode)
return false;
var point = view3D.mapToItem(iconMouseArea, x, y);
return point.x >= iconMouseArea.x && (point.x <= iconMouseArea.x + iconMouseArea.width)
&& point.y >= iconMouseArea.y && (point.y <= iconMouseArea.y + iconMouseArea.height);
}
onSelectedChanged: { onSelectedChanged: {
if (selected) if (selected)
hasMouse = false; hasMouse = false;

View File

@@ -272,7 +272,7 @@ void Qt5InformationNodeInstanceServer::handleInputEvents()
// Context menu requested // Context menu requested
if (command.button() == Qt::RightButton && command.modifiers() == Qt::NoModifier) if (command.button() == Qt::RightButton && command.modifiers() == Qt::NoModifier)
getModelAtPos(command.pos()); getNodeAtPos(command.pos());
} }
} }
@@ -386,7 +386,7 @@ void Qt5InformationNodeInstanceServer::removeRotationBlocks(
#endif #endif
} }
void Qt5InformationNodeInstanceServer::getModelAtPos(const QPointF &pos) void Qt5InformationNodeInstanceServer::getNodeAtPos(const QPointF &pos)
{ {
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
// pick a Quick3DModel at view position // pick a Quick3DModel at view position
@@ -398,13 +398,25 @@ void Qt5InformationNodeInstanceServer::getModelAtPos(const QPointF &pos)
QObject *obj = qvariant_cast<QObject *>(editViewProp.read()); QObject *obj = qvariant_cast<QObject *>(editViewProp.read());
QQuick3DViewport *editView = qobject_cast<QQuick3DViewport *>(obj); QQuick3DViewport *editView = qobject_cast<QQuick3DViewport *>(obj);
QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit(); // Non-model nodes with icon gizmos are also valid results
QVariant gizmoVar;
QMetaObject::invokeMethod(m_editView3DData.rootItem, "gizmoAt", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, gizmoVar),
Q_ARG(QVariant, pos.x()),
Q_ARG(QVariant, pos.y()));
QObject *gizmoObj = qvariant_cast<QObject *>(gizmoVar);
QVariant instance = -1;
// filter out picks of models created dynamically or inside components if (gizmoObj && hasInstanceForObject(gizmoObj)) {
QQuick3DModel *resolvedPick = qobject_cast<QQuick3DModel *>(helper->resolvePick(hitModel)); instance = instanceForObject(gizmoObj).instanceId();
} else {
QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit();
QObject *resolvedPick = helper->resolvePick(hitModel);
if (hasInstanceForObject(resolvedPick))
instance = instanceForObject(resolvedPick).instanceId();
}
QVariant instance = resolvedPick ? instanceForObject(resolvedPick).instanceId() : -1; nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, instance});
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ModelAtPos, instance});
#else #else
Q_UNUSED(pos) Q_UNUSED(pos)
#endif #endif
@@ -2387,8 +2399,8 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
break; break;
#endif #endif
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
case View3DActionCommand::GetModelAtPos: { case View3DActionCommand::GetNodeAtPos: {
getModelAtPos(command.value().toPointF()); getNodeAtPos(command.value().toPointF());
return; return;
} }
#endif #endif

View File

@@ -126,7 +126,7 @@ private:
void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges); void updateMaterialPreviewData(const QVector<PropertyValueContainer> &valueChanges);
void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges); void updateRotationBlocks(const QVector<PropertyValueContainer> &valueChanges);
void removeRotationBlocks(const QVector<qint32> &instanceIds); void removeRotationBlocks(const QVector<qint32> &instanceIds);
void getModelAtPos(const QPointF &pos); void getNodeAtPos(const QPointF &pos);
void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData); void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData);
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE

View File

@@ -41,6 +41,10 @@ PropertyEditorPane {
Item { width: 1; height: 10 } Item { width: 1; height: 10 }
DynamicPropertiesSection {
propertiesModel: MaterialEditorDynamicPropertiesModel {}
}
Loader { Loader {
id: specificsTwo id: specificsTwo
@@ -57,7 +61,11 @@ PropertyEditorPane {
} }
} }
Item { width: 1; height: 10 } Item {
width: 1
height: 10
visible: specificsTwo.visible
}
Loader { Loader {
id: specificsOne id: specificsOne

View File

@@ -0,0 +1,17 @@
PropertyLabel {
text: "%1"
tooltip: "%1"
}
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "QtQuick3D.%3"
validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.%2
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
width: implicitWidth
}
ExpandingSpacer {}
}

View File

@@ -56,4 +56,25 @@ AutoTypes {
sourceFile: "ImageEditorTemplate.template" sourceFile: "ImageEditorTemplate.template"
separateSection: true separateSection: true
} }
Type {
typeNames: ["TextureInput", "Texture"]
sourceFile: "3DItemFilterComboBoxEditorTemplate.template"
needsTypeArg: true
}
Type {
typeNames: ["vector2d"]
sourceFile: "Vector2dEditorTemplate.template"
}
Type {
typeNames: ["vector3d"]
sourceFile: "Vector3dEditorTemplate.template"
}
Type {
typeNames: ["vector4d"]
sourceFile: "Vector4dEditorTemplate.template"
}
} }

View File

@@ -0,0 +1,42 @@
PropertyLabel {
text: "%1"
tooltip: "%1"
}
ColumnLayout {
SecondColumnLayout {
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_x
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_y
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
}
ExpandingSpacer {}
}
}

View File

@@ -0,0 +1,61 @@
PropertyLabel {
text: "%1"
tooltip: "%1"
}
ColumnLayout {
SecondColumnLayout {
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_x
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_y
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
}
ExpandingSpacer {}
}
SecondColumnLayout {
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_z
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Z"
}
ExpandingSpacer {}
}
}

View File

@@ -0,0 +1,78 @@
PropertyLabel {
text: "%1"
tooltip: "%1"
}
ColumnLayout {
SecondColumnLayout {
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_x
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "X"
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_y
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Y"
}
ExpandingSpacer {}
}
SecondColumnLayout {
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_z
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "Z"
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
SpinBox {
minimumValue: -9999999
maximumValue: 9999999
decimals: 2
backendValue: backendValues.%2_w
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
}
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
ControlLabel {
text: "W"
}
ExpandingSpacer {}
}
}

View File

@@ -15,6 +15,10 @@ PropertyEditorPane {
showState: true showState: true
} }
DynamicPropertiesSection {
propertiesModel: SelectionDynamicPropertiesModel {}
}
GeometrySection {} GeometrySection {}
Section { Section {

View File

@@ -18,6 +18,10 @@ PropertyEditorPane {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
DynamicPropertiesSection {
propertiesModel: SelectionDynamicPropertiesModel {}
}
Loader { Loader {
id: specificsTwo id: specificsTwo
anchors.left: parent.left anchors.left: parent.left

View File

@@ -44,6 +44,10 @@ SecondColumnLayout {
colorEditor.backendValue.resetValue() colorEditor.backendValue.resetValue()
} }
function initEditor() {
cePopup.initEditor()
}
Connections { Connections {
id: backendConnection id: backendConnection
target: colorEditor target: colorEditor

View File

@@ -22,6 +22,23 @@ T.Popup {
property bool isInValidState: false 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() { function commitGradientColor() {
var hexColor = convertColorToString(colorEditor.color) var hexColor = convertColorToString(colorEditor.color)
cePopup.popupHexTextField.text = hexColor cePopup.popupHexTextField.text = hexColor
@@ -453,24 +470,10 @@ T.Popup {
} }
} }
} }
Connections { Connections {
target: modelNodeBackend target: modelNodeBackend
function onSelectionChanged() { function onSelectionChanged() {
if (colorEditor.supportGradient && gradientModel.hasGradient) { cePopup.initEditor()
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()
} }
} }
} }

View File

@@ -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 {}
}
}

View File

@@ -57,7 +57,7 @@ HelperWidgets.ComboBox {
comboBox.currentIndex = comboBox.find(text) comboBox.currentIndex = comboBox.find(text)
if (text === "") { if (text === "" || text === "null") {
comboBox.currentIndex = 0 comboBox.currentIndex = 0
comboBox.editText = comboBox.defaultItem comboBox.editText = comboBox.defaultItem
} else { } else {

View File

@@ -40,6 +40,10 @@ Item {
} }
} }
onBackendValueChanged: {
spinBox.enabled = backendValue === undefined ? false : !isBlocked(backendValue.name)
}
StudioControls.RealSpinBox { StudioControls.RealSpinBox {
id: spinBox id: spinBox

View File

@@ -20,6 +20,7 @@ ComponentSection 2.0 ComponentSection.qml
ControlLabel 2.0 ControlLabel.qml ControlLabel 2.0 ControlLabel.qml
singleton Controller 2.0 Controller.qml singleton Controller 2.0 Controller.qml
DoubleSpinBox 2.0 DoubleSpinBox.qml DoubleSpinBox 2.0 DoubleSpinBox.qml
DynamicPropertiesSection 2.0 DynamicPropertiesSection.qml
EditableListView 2.0 EditableListView.qml EditableListView 2.0 EditableListView.qml
ExpandingSpacer 2.0 ExpandingSpacer.qml ExpandingSpacer 2.0 ExpandingSpacer.qml
ExpressionTextField 2.0 ExpressionTextField.qml ExpressionTextField 2.0 ExpressionTextField.qml

View File

@@ -20,13 +20,17 @@ Project {
directory: "content" directory: "content"
} }
JavaScriptFiles { JavaScriptFiles {
directory: "imports" directory: "imports"
} }
ImageFiles { ImageFiles {
directory: "content" directory: "content"
} }
ImageFiles {
directory: "asset_imports"
}
Files { Files {
filter: "*.conf" filter: "*.conf"
@@ -95,7 +99,7 @@ Project {
/* Required for deployment */ /* Required for deployment */
targetDirectory: "/opt/%{ProjectName}" targetDirectory: "/opt/%{ProjectName}"
qdsVersion: "3.5" qdsVersion: "3.7"
quickVersion: "%{QtQuickVersion}" quickVersion: "%{QtQuickVersion}"

View File

@@ -372,6 +372,7 @@ extend_qtc_plugin(QmlDesigner
fileresourcesmodel.cpp fileresourcesmodel.h fileresourcesmodel.cpp fileresourcesmodel.h
itemfiltermodel.cpp itemfiltermodel.h itemfiltermodel.cpp itemfiltermodel.h
gradientmodel.cpp gradientmodel.h gradientmodel.cpp gradientmodel.h
dynamicpropertiesproxymodel.cpp dynamicpropertiesproxymodel.h
gradientpresetcustomlistmodel.cpp gradientpresetcustomlistmodel.h gradientpresetcustomlistmodel.cpp gradientpresetcustomlistmodel.h
gradientpresetdefaultlistmodel.cpp gradientpresetdefaultlistmodel.h gradientpresetdefaultlistmodel.cpp gradientpresetdefaultlistmodel.h
gradientpresetitem.cpp gradientpresetitem.h gradientpresetitem.cpp gradientpresetitem.h
@@ -393,6 +394,7 @@ extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/materialeditor SOURCES_PREFIX components/materialeditor
SOURCES SOURCES
materialeditorcontextobject.cpp materialeditorcontextobject.h materialeditorcontextobject.cpp materialeditorcontextobject.h
materialeditordynamicpropertiesproxymodel.cpp materialeditordynamicpropertiesproxymodel.h
materialeditorqmlbackend.cpp materialeditorqmlbackend.h materialeditorqmlbackend.cpp materialeditorqmlbackend.h
materialeditortransaction.cpp materialeditortransaction.h materialeditortransaction.cpp materialeditortransaction.h
materialeditorview.cpp materialeditorview.h materialeditorview.cpp materialeditorview.h
@@ -405,6 +407,7 @@ extend_qtc_plugin(QmlDesigner
materialbrowserview.cpp materialbrowserview.h materialbrowserview.cpp materialbrowserview.h
materialbrowserwidget.cpp materialbrowserwidget.h materialbrowserwidget.cpp materialbrowserwidget.h
materialbrowsermodel.cpp materialbrowsermodel.h materialbrowsermodel.cpp materialbrowsermodel.h
bundleimporter.cpp bundleimporter.h
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner
@@ -560,6 +563,7 @@ extend_qtc_plugin(QmlDesigner
connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui
delegates.cpp delegates.h delegates.cpp delegates.h
dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h
selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner

View File

@@ -60,41 +60,52 @@ bool Navigation2dFilter::gestureEvent(QGestureEvent *event)
bool Navigation2dFilter::wheelEvent(QWheelEvent *event) bool Navigation2dFilter::wheelEvent(QWheelEvent *event)
{ {
if (event->source() == Qt::MouseEventSynthesizedBySystem) { if (!event->modifiers().testFlag(Qt::ControlModifier)) {
if (event->modifiers().testFlag(Qt::ControlModifier)) { if (event->source() == Qt::MouseEventSynthesizedBySystem) {
if (QPointF delta = event->pixelDelta(); !delta.isNull()) {
double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y();
emit zoomChanged(dist/200.0, event->position());
event->accept();
return true;
}
} else {
emit panChanged(QPointF(event->pixelDelta())); emit panChanged(QPointF(event->pixelDelta()));
event->accept(); event->accept();
return true; return true;
} }
} else if (event->source() == Qt::MouseEventNotSynthesized) { return false;
}
auto zoomInSignal = QMetaMethod::fromSignal(&Navigation2dFilter::zoomIn); auto zoomChangedSignal = QMetaMethod::fromSignal(&Navigation2dFilter::zoomChanged);
bool zoomInConnected = QObject::isSignalConnected(zoomInSignal); bool zoomChangedConnected = QObject::isSignalConnected(zoomChangedSignal);
auto zoomOutSignal = QMetaMethod::fromSignal(&Navigation2dFilter::zoomOut); if (zoomChangedConnected) {
bool zoomOutConnected = QObject::isSignalConnected(zoomOutSignal); if (QPointF delta = event->pixelDelta(); !delta.isNull()) {
double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y();
emit zoomChanged(dist/200.0, event->position());
event->accept();
return true;
} else if (QPointF delta = event->angleDelta(); !delta.isNull()) {
double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y();
dist = dist / (8*15);
emit zoomChanged(dist/200.0, event->position());
event->accept();
return true;
}
return false;
}
if (zoomInConnected && zoomOutConnected) { auto zoomInSignal = QMetaMethod::fromSignal(&Navigation2dFilter::zoomIn);
if (event->modifiers().testFlag(Qt::ControlModifier)) { bool zoomInConnected = QObject::isSignalConnected(zoomInSignal);
if (QPointF angle = event->angleDelta(); !angle.isNull()) {
double delta = std::abs(angle.x()) > std::abs(angle.y()) ? angle.x() : angle.y(); auto zoomOutSignal = QMetaMethod::fromSignal(&Navigation2dFilter::zoomOut);
if (delta > 0) bool zoomOutConnected = QObject::isSignalConnected(zoomOutSignal);
emit zoomIn();
else if (zoomInConnected && zoomOutConnected) {
emit zoomOut(); if (QPointF angle = event->angleDelta(); !angle.isNull()) {
event->accept(); double delta = std::abs(angle.x()) > std::abs(angle.y()) ? angle.x() : angle.y();
return true; if (delta > 0)
} emit zoomIn();
} else
emit zoomOut();
event->accept();
return true;
} }
} }
return false; return false;
} }

View File

@@ -28,7 +28,7 @@ ConnectionView::ConnectionView(QObject *parent) : AbstractView(parent),
m_connectionViewWidget(new ConnectionViewWidget()), m_connectionViewWidget(new ConnectionViewWidget()),
m_connectionModel(new ConnectionModel(this)), m_connectionModel(new ConnectionModel(this)),
m_bindingModel(new BindingModel(this)), m_bindingModel(new BindingModel(this)),
m_dynamicPropertiesModel(new DynamicPropertiesModel(this)), m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)),
m_backendModel(new BackendModel(this)) m_backendModel(new BackendModel(this))
{ {
connectionViewWidget()->setBindingModel(m_bindingModel); connectionViewWidget()->setBindingModel(m_bindingModel);
@@ -43,7 +43,7 @@ void ConnectionView::modelAttached(Model *model)
{ {
AbstractView::modelAttached(model); AbstractView::modelAttached(model);
bindingModel()->selectionChanged(QList<ModelNode>()); bindingModel()->selectionChanged(QList<ModelNode>());
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>()); dynamicPropertiesModel()->reset();
connectionModel()->resetModel(); connectionModel()->resetModel();
connectionViewWidget()->resetItemViews(); connectionViewWidget()->resetItemViews();
backendModel()->resetModel(); backendModel()->resetModel();
@@ -53,7 +53,7 @@ void ConnectionView::modelAboutToBeDetached(Model *model)
{ {
AbstractView::modelAboutToBeDetached(model); AbstractView::modelAboutToBeDetached(model);
bindingModel()->selectionChanged(QList<ModelNode>()); bindingModel()->selectionChanged(QList<ModelNode>());
dynamicPropertiesModel()->selectionChanged(QList<ModelNode>()); dynamicPropertiesModel()->reset();
connectionModel()->resetModel(); connectionModel()->resetModel();
connectionViewWidget()->resetItemViews(); connectionViewWidget()->resetItemViews();
} }
@@ -99,7 +99,7 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &p
bindingModel()->bindingRemoved(property.toBindingProperty()); bindingModel()->bindingRemoved(property.toBindingProperty());
dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty()); dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty());
} else if (property.isVariantProperty()) { } else if (property.isVariantProperty()) {
//### dynamicPropertiesModel->bindingRemoved(property.toVariantProperty()); dynamicPropertiesModel()->variantRemoved(property.toVariantProperty());
} else if (property.isSignalHandlerProperty()) { } else if (property.isSignalHandlerProperty()) {
connectionModel()->removeRowFromTable(property.toSignalHandlerProperty()); connectionModel()->removeRowFromTable(property.toSignalHandlerProperty());
} }
@@ -145,7 +145,7 @@ void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeL
const QList<ModelNode> & /*lastSelectedNodeList*/) const QList<ModelNode> & /*lastSelectedNodeList*/)
{ {
bindingModel()->selectionChanged(selectedNodeList); bindingModel()->selectionChanged(selectedNodeList);
dynamicPropertiesModel()->selectionChanged(selectedNodeList); dynamicPropertiesModel()->reset();
connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex()); connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex());
connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex()); connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex());
@@ -184,6 +184,11 @@ void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, cons
backendModel()->resetModel(); backendModel()->resetModel();
} }
void ConnectionView::currentStateChanged(const ModelNode &node)
{
dynamicPropertiesModel()->reset();
}
WidgetInfo ConnectionView::widgetInfo() WidgetInfo ConnectionView::widgetInfo()
{ {
return createWidgetInfo(m_connectionViewWidget.data(), return createWidgetInfo(m_connectionViewWidget.data(),

View File

@@ -54,6 +54,8 @@ public:
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void currentStateChanged(const ModelNode &node) override;
WidgetInfo widgetInfo() override; WidgetInfo widgetInfo() override;
bool hasWidget() const override; bool hasWidget() const override;
bool isWidgetEnabled(); bool isWidgetEnabled();

View File

@@ -355,8 +355,8 @@ void ConnectionViewWidget::invalidateButtonStatus()
} else if (currentTab() == DynamicPropertiesTab) { } else if (currentTab() == DynamicPropertiesTab) {
emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection()); emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection());
auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model()); auto dynamicPropertiesModel = qobject_cast<DynamicPropertiesModel*>(ui->dynamicPropertiesView->model());
emit setEnabledAddButton(dynamicPropertiesModel->connectionView()->model() && emit setEnabledAddButton(dynamicPropertiesModel->view()->model() &&
dynamicPropertiesModel->connectionView()->selectedModelNodes().count() == 1); dynamicPropertiesModel->selectedNodes().count() == 1);
} else if (currentTab() == BackendTab) { } else if (currentTab() == BackendTab) {
emit setEnabledAddButton(true); emit setEnabledAddButton(true);
emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection()); emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection());
@@ -526,9 +526,9 @@ void ConnectionViewWidget::editorForDynamic()
QString newValue = m_dynamicEditor->bindingValue().trimmed(); QString newValue = m_dynamicEditor->bindingValue().trimmed();
if (m_dynamicIndex.isValid()) { if (m_dynamicIndex.isValid()) {
if (propertiesModel->connectionView()->isWidgetEnabled() if (qobject_cast<ConnectionView *>(propertiesModel->view())->isWidgetEnabled()
&& (propertiesModel->rowCount() > m_dynamicIndex.row())) { && (propertiesModel->rowCount() > m_dynamicIndex.row())) {
propertiesModel->connectionView()->executeInTransaction( propertiesModel->view()->executeInTransaction(
"ConnectionView::setBinding", [this, propertiesModel, newValue]() { "ConnectionView::setBinding", [this, propertiesModel, newValue]() {
AbstractProperty abProp = propertiesModel->abstractPropertyForRow( AbstractProperty abProp = propertiesModel->abstractPropertyForRow(
m_dynamicIndex.row()); m_dynamicIndex.row());

View File

@@ -187,11 +187,11 @@ QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOp
return widget; return widget;
} }
if (!model->connectionView()) { if (!model->view()) {
qWarning() << "BindingDelegate::createEditor no connection view"; qWarning() << "BindingDelegate::createEditor no connection view";
return widget; return widget;
} }
model->connectionView()->allModelNodes(); model->view()->allModelNodes();
switch (index.column()) { switch (index.column()) {
case DynamicPropertiesModel::TargetModelNodeRow: { case DynamicPropertiesModel::TargetModelNodeRow: {
@@ -209,7 +209,7 @@ QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOp
}); });
dynamicPropertiesComboBox->addItem(QLatin1String("alias")); dynamicPropertiesComboBox->addItem(QLatin1String("alias"));
//dynamicPropertiesComboBox->addItem(QLatin1String("Item")); dynamicPropertiesComboBox->addItem(QLatin1String("Item"));
dynamicPropertiesComboBox->addItem(QLatin1String("real")); dynamicPropertiesComboBox->addItem(QLatin1String("real"));
dynamicPropertiesComboBox->addItem(QLatin1String("int")); dynamicPropertiesComboBox->addItem(QLatin1String("int"));
dynamicPropertiesComboBox->addItem(QLatin1String("string")); dynamicPropertiesComboBox->addItem(QLatin1String("string"));
@@ -217,6 +217,10 @@ QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOp
dynamicPropertiesComboBox->addItem(QLatin1String("url")); dynamicPropertiesComboBox->addItem(QLatin1String("url"));
dynamicPropertiesComboBox->addItem(QLatin1String("color")); dynamicPropertiesComboBox->addItem(QLatin1String("color"));
dynamicPropertiesComboBox->addItem(QLatin1String("variant")); dynamicPropertiesComboBox->addItem(QLatin1String("variant"));
dynamicPropertiesComboBox->addItem(QLatin1String("TextureInput"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector2d"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector3d"));
dynamicPropertiesComboBox->addItem(QLatin1String("vector4d"));
return dynamicPropertiesComboBox; return dynamicPropertiesComboBox;
}; };
case DynamicPropertiesModel::PropertyValueRow: { case DynamicPropertiesModel::PropertyValueRow: {

View File

@@ -16,6 +16,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QMessageBox> #include <QMessageBox>
#include <QTimer> #include <QTimer>
@@ -69,8 +70,18 @@ QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::T
} else { } else {
returnValue = QColor(Qt::black); 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") { } else if (typeName == "Item") {
returnValue = 0; returnValue = "null";
} }
return returnValue; return returnValue;
@@ -86,7 +97,7 @@ QmlDesigner::PropertyName DynamicPropertiesModel::unusedProperty(const QmlDesign
{ {
QmlDesigner::PropertyName propertyName = "property"; QmlDesigner::PropertyName propertyName = "property";
int i = 0; int i = 0;
if (modelNode.metaInfo().isValid()) { if (modelNode.isValid() && modelNode.metaInfo().isValid()) {
while (true) { while (true) {
const QmlDesigner::PropertyName currentPropertyName = propertyName + QString::number(i).toLatin1(); const QmlDesigner::PropertyName currentPropertyName = propertyName + QString::number(i).toLatin1();
if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName)) if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName))
@@ -98,9 +109,18 @@ QmlDesigner::PropertyName DynamicPropertiesModel::unusedProperty(const QmlDesign
return propertyName; 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) : QStandardItemModel(parent)
, m_connectionView(parent) , m_view(parent)
, m_explicitSelection(explicitSelection)
{ {
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged); connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
} }
@@ -112,8 +132,9 @@ void DynamicPropertiesModel::resetModel()
setHorizontalHeaderLabels( setHorizontalHeaderLabels(
QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")})); QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}));
if (connectionView()->isAttached()) { if (m_view->isAttached()) {
for (const ModelNode &modelNode : connectionView()->selectedModelNodes()) const auto nodes = selectedNodes();
for (const ModelNode &modelNode : nodes)
addModelNode(modelNode); addModelNode(modelNode);
} }
@@ -125,8 +146,8 @@ void DynamicPropertiesModel::resetModel()
//Value copying is optional //Value copying is optional
BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue) BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue)
{ {
if (connectionView()->selectedModelNodes().count() == 1) { if (selectedNodes().count() == 1) {
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst(); const ModelNode modelNode = selectedNodes().constFirst();
if (modelNode.isValid()) { if (modelNode.isValid()) {
if (modelNode.hasVariantProperty(name)) { if (modelNode.hasVariantProperty(name)) {
try { try {
@@ -160,8 +181,8 @@ BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const Property
//If it's a BindingProperty, then replaces it with empty VariantProperty //If it's a BindingProperty, then replaces it with empty VariantProperty
void DynamicPropertiesModel::resetProperty(const PropertyName &name) void DynamicPropertiesModel::resetProperty(const PropertyName &name)
{ {
if (connectionView()->selectedModelNodes().count() == 1) { if (selectedNodes().count() == 1) {
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst(); const ModelNode modelNode = selectedNodes().constFirst();
if (modelNode.isValid()) { if (modelNode.isValid()) {
if (modelNode.hasProperty(name)) { if (modelNode.hasProperty(name)) {
try { try {
@@ -169,11 +190,10 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
if (abProp.isVariantProperty()) { if (abProp.isVariantProperty()) {
VariantProperty property = abProp.toVariantProperty(); VariantProperty property = abProp.toVariantProperty();
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), property.dynamicTypeName()); QVariant newValue = convertVariantForTypeName({}, property.dynamicTypeName());
property.setDynamicTypeNameAndValue(property.dynamicTypeName(), property.setDynamicTypeNameAndValue(property.dynamicTypeName(),
newValue); newValue);
} } else if (abProp.isBindingProperty()) {
else if (abProp.isBindingProperty()) {
BindingProperty property = abProp.toBindingProperty(); BindingProperty property = abProp.toBindingProperty();
TypeName oldType = property.dynamicTypeName(); TypeName oldType = property.dynamicTypeName();
@@ -181,9 +201,8 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
modelNode.removeProperty(name); modelNode.removeProperty(name);
VariantProperty newProperty = modelNode.variantProperty(name); VariantProperty newProperty = modelNode.variantProperty(name);
QVariant newValue = convertVariantForTypeName(QVariant("none.none"), oldType); QVariant newValue = convertVariantForTypeName({}, oldType);
newProperty.setDynamicTypeNameAndValue(oldType, newProperty.setDynamicTypeNameAndValue(oldType, newValue);
newValue);
} }
} catch (RewritingException &e) { } catch (RewritingException &e) {
@@ -205,8 +224,8 @@ void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindi
m_handleDataChanged = false; m_handleDataChanged = false;
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes(); const QList<ModelNode> nodes = selectedNodes();
if (!selectedNodes.contains(bindingProperty.parentModelNode())) if (!nodes.contains(bindingProperty.parentModelNode()))
return; return;
if (!m_lock) { if (!m_lock) {
int rowNumber = findRowForBindingProperty(bindingProperty); int rowNumber = findRowForBindingProperty(bindingProperty);
@@ -228,17 +247,16 @@ void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &varia
m_handleDataChanged = false; m_handleDataChanged = false;
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes(); const QList<ModelNode> nodes = selectedNodes();
if (!selectedNodes.contains(variantProperty.parentModelNode())) if (!nodes.contains(variantProperty.parentModelNode()))
return; return;
if (!m_lock) { if (!m_lock) {
int rowNumber = findRowForVariantProperty(variantProperty); int rowNumber = findRowForVariantProperty(variantProperty);
if (rowNumber == -1) { if (rowNumber == -1)
addVariantProperty(variantProperty); addVariantProperty(variantProperty);
} else { else
updateVariantProperty(rowNumber); updateVariantProperty(rowNumber);
}
} }
m_handleDataChanged = true; m_handleDataChanged = true;
@@ -248,8 +266,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
{ {
m_handleDataChanged = false; m_handleDataChanged = false;
QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes(); const QList<ModelNode> nodes = selectedNodes();
if (!selectedNodes.contains(bindingProperty.parentModelNode())) if (!nodes.contains(bindingProperty.parentModelNode()))
return; return;
if (!m_lock) { if (!m_lock) {
int rowNumber = findRowForBindingProperty(bindingProperty); int rowNumber = findRowForBindingProperty(bindingProperty);
@@ -259,16 +277,36 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
m_handleDataChanged = true; m_handleDataChanged = true;
} }
void DynamicPropertiesModel::selectionChanged([[maybe_unused]] 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()
{ {
m_handleDataChanged = false; m_handleDataChanged = false;
resetModel(); resetModel();
m_handleDataChanged = true; 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 AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const
@@ -276,10 +314,10 @@ AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) c
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
if (!connectionView()->isAttached()) if (!m_view->isAttached())
return AbstractProperty(); return AbstractProperty();
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); ModelNode modelNode = m_view->modelNodeForInternalId(internalId);
if (modelNode.isValid()) if (modelNode.isValid())
return modelNode.property(targetPropertyName.toUtf8()); return modelNode.property(targetPropertyName.toUtf8());
@@ -292,7 +330,7 @@ BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) con
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); 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()) if (modelNode.isValid())
return modelNode.bindingProperty(targetPropertyName.toUtf8()); return modelNode.bindingProperty(targetPropertyName.toUtf8());
@@ -305,7 +343,7 @@ VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) con
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); 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()) if (modelNode.isValid())
return modelNode.variantProperty(targetPropertyName.toUtf8()); return modelNode.variantProperty(targetPropertyName.toUtf8());
@@ -341,8 +379,8 @@ void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
{ {
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED);
if (connectionView()->selectedModelNodes().count() == 1) { if (selectedNodes().count() == 1) {
const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst(); const ModelNode modelNode = selectedNodes().constFirst();
if (modelNode.isValid()) { if (modelNode.isValid()) {
try { try {
modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none")); modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
@@ -396,16 +434,14 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber) void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
{ {
connectionView()->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() { m_view->executeInTransaction("DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
if (bindingProperty.isValid()) { if (bindingProperty.isValid()) {
bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
} } else {
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
VariantProperty variantProperty = variantPropertyForRow(rowNumber); if (variantProperty.isValid())
variantProperty.parentModelNode().removeProperty(variantProperty.name());
if (variantProperty.isValid()) {
variantProperty.parentModelNode().removeProperty(variantProperty.name());
} }
}); });
@@ -431,7 +467,6 @@ void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
items.append(idItem); items.append(idItem);
items.append(propertyNameItem); items.append(propertyNameItem);
propertyTypeItem = new QStandardItem(propertyType); propertyTypeItem = new QStandardItem(propertyType);
items.append(propertyTypeItem); items.append(propertyTypeItem);
@@ -487,6 +522,9 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode) void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode)
{ {
if (!modelNode.isValid())
return;
const QList<BindingProperty> bindingProperties = modelNode.bindingProperties(); const QList<BindingProperty> bindingProperties = modelNode.bindingProperties();
for (const BindingProperty &bindingProperty : bindingProperties) { for (const BindingProperty &bindingProperty : bindingProperties) {
if (bindingProperty.isDynamic()) if (bindingProperty.isDynamic())
@@ -507,7 +545,7 @@ void DynamicPropertiesModel::updateValue(int row)
if (bindingProperty.isBindingProperty()) { if (bindingProperty.isBindingProperty()) {
const QString expression = data(index(row, PropertyValueRow)).toString(); const QString expression = data(index(row, PropertyValueRow)).toString();
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue")); RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
try { try {
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression); bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
transaction.commit(); //committing in the try block transaction.commit(); //committing in the try block
@@ -523,7 +561,7 @@ void DynamicPropertiesModel::updateValue(int row)
if (variantProperty.isVariantProperty()) { if (variantProperty.isVariantProperty()) {
const QVariant value = data(index(row, PropertyValueRow)); const QVariant value = data(index(row, PropertyValueRow));
RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue")); RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
try { try {
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
transaction.commit(); //committing in the try block transaction.commit(); //committing in the try block
@@ -547,7 +585,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
ModelNode targetNode = bindingProperty.parentModelNode(); ModelNode targetNode = bindingProperty.parentModelNode();
if (bindingProperty.isBindingProperty()) { if (bindingProperty.isBindingProperty()) {
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){ m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
const QString expression = bindingProperty.expression(); const QString expression = bindingProperty.expression();
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
@@ -566,7 +604,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
ModelNode targetNode = variantProperty.parentModelNode(); ModelNode targetNode = variantProperty.parentModelNode();
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){ m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value); targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
targetNode.removeProperty(variantProperty.name()); targetNode.removeProperty(variantProperty.name());
}); });
@@ -592,7 +630,7 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
const PropertyName propertyName = bindingProperty.name(); const PropertyName propertyName = bindingProperty.name();
ModelNode targetNode = bindingProperty.parentModelNode(); ModelNode targetNode = bindingProperty.parentModelNode();
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){ m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
targetNode.removeProperty(bindingProperty.name()); targetNode.removeProperty(bindingProperty.name());
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression); targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
}); });
@@ -608,12 +646,14 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
ModelNode targetNode = variantProperty.parentModelNode(); ModelNode targetNode = variantProperty.parentModelNode();
const PropertyName propertyName = variantProperty.name(); const PropertyName propertyName = variantProperty.name();
connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){ m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
targetNode.removeProperty(variantProperty.name()); targetNode.removeProperty(variantProperty.name());
if (newType == "alias") { //alias properties have to be bindings if (!isValueType(newType)) {
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none")); targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(
newType, convertVariantForTypeName({}, newType).toString());
} else { } else {
targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType)); targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(
newType, convertVariantForTypeName(value, newType));
} }
}); });
@@ -632,7 +672,7 @@ ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const M
ModelNode modelNode; ModelNode modelNode;
if (id != QLatin1String("parent")) { if (id != QLatin1String("parent")) {
modelNode = connectionView()->modelNodeForId(id); modelNode = m_view->modelNodeForId(id);
} else { } else {
if (targetNode.hasParentProperty()) { if (targetNode.hasParentProperty()) {
modelNode = targetNode.parentProperty().parentModelNode(); modelNode = targetNode.parentProperty().parentModelNode();
@@ -752,6 +792,24 @@ void DynamicPropertiesModel::handleException()
resetModel(); 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 Internal
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -11,9 +11,9 @@
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { class AbstractView;
class ConnectionView; namespace Internal {
class DynamicPropertiesModel : public QStandardItemModel class DynamicPropertiesModel : public QStandardItemModel
{ {
@@ -26,13 +26,17 @@ public:
PropertyTypeRow = 2, PropertyTypeRow = 2,
PropertyValueRow = 3 PropertyValueRow = 3
}; };
DynamicPropertiesModel(ConnectionView *parent = nullptr); DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
void bindingPropertyChanged(const BindingProperty &bindingProperty); void bindingPropertyChanged(const BindingProperty &bindingProperty);
void variantPropertyChanged(const VariantProperty &variantProperty); void variantPropertyChanged(const VariantProperty &variantProperty);
void bindingRemoved(const BindingProperty &bindingProperty); 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; AbstractProperty abstractPropertyForRow(int rowNumber) const;
BindingProperty bindingPropertyForRow(int rowNumber) const; BindingProperty bindingPropertyForRow(int rowNumber) const;
VariantProperty variantPropertyForRow(int rowNumber) const; VariantProperty variantPropertyForRow(int rowNumber) const;
@@ -49,6 +53,8 @@ public:
QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode); QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode);
static bool isValueType(const TypeName &type);
protected: protected:
void addProperty(const QVariant &propertyValue, void addProperty(const QVariant &propertyValue,
const QString &propertyType, const QString &propertyType,
@@ -75,12 +81,12 @@ private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
void handleException(); void handleException();
private: AbstractView *m_view = nullptr;
ConnectionView *m_connectionView;
bool m_lock = false; bool m_lock = false;
bool m_handleDataChanged = false; bool m_handleDataChanged = false;
QString m_exceptionError; QString m_exceptionError;
QList<ModelNode> m_selectedNodes;
bool m_explicitSelection = false;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -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");
}

View File

@@ -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();
};

View File

@@ -219,7 +219,7 @@ void Edit3DView::handleEntriesChanged()
const QList<ItemLibraryEntry> itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries(); const QList<ItemLibraryEntry> itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries();
for (const ItemLibraryEntry &entry : itemLibEntries) { for (const ItemLibraryEntry &entry : itemLibEntries) {
if (entry.typeName() == "QtQuick3D.Model") { if (entry.typeName() == "QtQuick3D.Model" && entry.name() != "Empty") {
entriesMap[primitives].append(entry); entriesMap[primitives].append(entry);
} else if (entry.typeName() == "QtQuick3D.DirectionalLight" } else if (entry.typeName() == "QtQuick3D.DirectionalLight"
|| entry.typeName() == "QtQuick3D.PointLight" || entry.typeName() == "QtQuick3D.PointLight"
@@ -264,28 +264,31 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
} }
/** /**
* @brief get model at position from puppet process * @brief Get node at position from puppet process
* *
* Response from puppet process for the model at requested position * Response from puppet process for the model at requested position
* *
* @param modelNode 3D model picked at the requested position, invalid node if no model exists * @param modelNode Node picked at the requested position or invalid node if nothing could be picked
*/ */
void Edit3DView::modelAtPosReady(const ModelNode &modelNode) void Edit3DView::nodeAtPosReady(const ModelNode &modelNode)
{ {
if (m_modelAtPosReqType == ModelAtPosReqType::ContextMenu) { if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) {
// Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item // Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item
// while the context-menu is shown doesn't select the item. // while the context-menu is shown doesn't select the item.
if (modelNode.isValid() && !modelNode.isSelected()) if (modelNode.isValid() && !modelNode.isSelected())
setSelectedModelNode(modelNode); setSelectedModelNode(modelNode);
m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode); m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode);
} else if (m_modelAtPosReqType == ModelAtPosReqType::MaterialDrop) { } else if (m_nodeAtPosReqType == NodeAtPosReqType::MaterialDrop) {
if (m_droppedMaterial.isValid() && modelNode.isValid()) { // TODO: this is from 8.0 branch that doesn't apply anymore:
// const bool isModel = modelNode.isSubclassOf("QtQuick3D.Model");
const bool isModel = false;
if (m_droppedMaterial.isValid() && modelNode.isValid() && isModel) {
executeInTransaction(__FUNCTION__, [&] { executeInTransaction(__FUNCTION__, [&] {
assignMaterialTo3dModel(modelNode, m_droppedMaterial); assignMaterialTo3dModel(modelNode, m_droppedMaterial);
}); });
} }
} }
m_modelAtPosReqType = ModelAtPosReqType::None; m_nodeAtPosReqType = NodeAtPosReqType::None;
} }
void Edit3DView::sendInputEvent(QInputEvent *e) const void Edit3DView::sendInputEvent(QInputEvent *e) const
@@ -671,18 +674,18 @@ void Edit3DView::addQuick3DImport()
} }
// This method is called upon right-clicking the view to prepare for context-menu creation. The actual // This method is called upon right-clicking the view to prepare for context-menu creation. The actual
// context menu is created when modelAtPosReady() is received from puppet // context menu is created when nodeAtPosReady() is received from puppet
void Edit3DView::startContextMenu(const QPoint &pos) void Edit3DView::startContextMenu(const QPoint &pos)
{ {
m_contextMenuPos = pos; m_contextMenuPos = pos;
m_modelAtPosReqType = ModelAtPosReqType::ContextMenu; m_nodeAtPosReqType = NodeAtPosReqType::ContextMenu;
} }
void Edit3DView::dropMaterial(const ModelNode &matNode, const QPointF &pos) void Edit3DView::dropMaterial(const ModelNode &matNode, const QPointF &pos)
{ {
m_modelAtPosReqType = ModelAtPosReqType::MaterialDrop; m_nodeAtPosReqType = NodeAtPosReqType::MaterialDrop;
m_droppedMaterial = matNode; m_droppedMaterial = matNode;
QmlDesignerPlugin::instance()->viewManager().nodeInstanceView()->view3DAction({View3DActionCommand::GetModelAtPos, pos}); QmlDesignerPlugin::instance()->viewManager().nodeInstanceView()->view3DAction({View3DActionCommand::GetNodeAtPos, pos});
} }
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -41,7 +41,7 @@ public:
void modelAboutToBeDetached(Model *model) override; void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void modelAtPosReady(const ModelNode &modelNode) override; void nodeAtPosReady(const ModelNode &modelNode) override;
void sendInputEvent(QInputEvent *e) const; void sendInputEvent(QInputEvent *e) const;
void edit3DViewResized(const QSize &size) const; void edit3DViewResized(const QSize &size) const;
@@ -63,7 +63,7 @@ private slots:
void onEntriesChanged(); void onEntriesChanged();
private: private:
enum class ModelAtPosReqType { enum class NodeAtPosReqType {
MaterialDrop, MaterialDrop,
ContextMenu, ContextMenu,
None None
@@ -108,7 +108,7 @@ private:
int particlemode; int particlemode;
ModelCache<QImage> m_canvasCache; ModelCache<QImage> m_canvasCache;
ModelNode m_droppedMaterial; ModelNode m_droppedMaterial;
ModelAtPosReqType m_modelAtPosReqType; NodeAtPosReqType m_nodeAtPosReqType;
QPoint m_contextMenuPos; QPoint m_contextMenuPos;
QTimer m_compressionTimer; QTimer m_compressionTimer;
}; };

View File

@@ -278,8 +278,12 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
{ {
m_contextMenuTarget = modelNode; m_contextMenuTarget = modelNode;
m_editMaterialAction->setEnabled(modelNode.isValid()); const bool isValid = modelNode.isValid();
m_deleteAction->setEnabled(modelNode.isValid()); // TODO: this is from 8.0 branch that doesn't apply anymore:
// const bool isModel = isValid && modelNode.isSubclassOf("QtQuick3D.Model");
const bool isModel = false;
m_editMaterialAction->setEnabled(isModel);
m_deleteAction->setEnabled(isValid && !modelNode.isRootNode());
m_contextMenu->popup(mapToGlobal(pos)); m_contextMenu->popup(mapToGlobal(pos));
} }

View File

@@ -0,0 +1,235 @@
/****************************************************************************
**
** 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 "bundleimporter.h"
#include "import.h"
#include "model.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "rewritingexception.h"
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <QFile>
#include <QFileInfo>
#include <QSaveFile>
#include <QStringList>
using namespace Utils;
namespace QmlDesigner::Internal {
BundleImporter::BundleImporter(const QString &bundleDir,
const QString &bundleId,
const QStringList &sharedFiles,
QObject *parent)
: QObject(parent)
, m_bundleDir(FilePath::fromString(bundleDir))
, m_bundleId(bundleId)
, m_sharedFiles(sharedFiles)
{
m_importTimer.setInterval(200);
connect(&m_importTimer, &QTimer::timeout, this, &BundleImporter::handleImportTimer);
m_moduleName = QStringLiteral("%1.%2").arg(
QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
m_bundleId).mid(1); // Chop leading slash
}
// Returns empty string on success or an error message on failure.
// Note that there is also an asynchronous portion to the import, which will only
// be done if this method returns success. Once the asynchronous portion of the
// import is completed, importFinished signal will be emitted.
QString BundleImporter::importComponent(const QString &qmlFile,
const QStringList &files)
{
FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
if (bundleImportPath.isEmpty())
return "Failed to resolve current project path";
const QString projectBundlePath = QStringLiteral("%1%2/%3").arg(
QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER),
QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
m_bundleId).mid(1); // Chop leading slash
bundleImportPath = bundleImportPath.resolvePath(projectBundlePath);
if (!bundleImportPath.exists()) {
if (!bundleImportPath.createDir())
return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString());
}
for (const QString &file : qAsConst(m_sharedFiles)) {
FilePath target = bundleImportPath.resolvePath(file);
if (!target.exists()) {
FilePath parentDir = target.parentDir();
if (!parentDir.exists() && !parentDir.createDir())
return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
FilePath source = m_bundleDir.resolvePath(file);
if (!source.copyFile(target))
return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString());
}
}
FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir"));
QFile qmldirFile(qmldirPath.toString());
QString qmldirContent;
if (qmldirPath.exists()) {
if (!qmldirFile.open(QIODeviceBase::ReadOnly))
return QStringLiteral("Failed to open qmldir file for reading: '%1'").arg(qmldirPath.toString());
qmldirContent = QString::fromUtf8(qmldirFile.readAll());
qmldirFile.close();
} else {
qmldirContent.append("module ");
qmldirContent.append(m_moduleName);
qmldirContent.append('\n');
}
FilePath qmlSourceFile = FilePath::fromString(qmlFile);
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
m_pendingTypes.append(QStringLiteral("%1.%2")
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), qmlType));
if (!qmldirContent.contains(qmlFile)) {
QSaveFile qmldirSaveFile(qmldirPath.toString());
if (!qmldirSaveFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Truncate))
return QStringLiteral("Failed to open qmldir file for writing: '%1'").arg(qmldirPath.toString());
qmldirContent.append(qmlType);
qmldirContent.append(" 1.0 ");
qmldirContent.append(qmlFile);
qmldirContent.append('\n');
qmldirSaveFile.write(qmldirContent.toUtf8());
qmldirSaveFile.commit();
}
QStringList allFiles;
allFiles.append(files);
allFiles.append(qmlFile);
for (const QString &file : qAsConst(allFiles)) {
FilePath target = bundleImportPath.resolvePath(file);
FilePath parentDir = target.parentDir();
if (!parentDir.exists() && !parentDir.createDir())
return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
FilePath source = m_bundleDir.resolvePath(file);
if (target.exists()) {
if (source.lastModified() == target.lastModified())
continue;
target.removeFile(); // Remove existing file for update
}
if (!source.copyFile(target))
return QStringLiteral("Failed to copy file: '%1'").arg(source.toString());
}
m_fullReset = !qmlFileExists;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (!model)
return "Model not available, cannot add import statement or update code model";
Import import = Import::createLibraryImport(m_moduleName, "1.0");
if (!model->hasImport(import)) {
if (model->possibleImports().contains(import)) {
m_importAddPending = false;
try {
model->changeImports({import}, {});
} catch (const RewritingException &) {
// No point in trying to add import asynchronously either, so just fail out
return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName);
}
} else {
// If import is not yet possible, import statement needs to be added asynchronously to
// avoid errors, as code model update takes a while. Full reset is not necessary
// in this case, as new import directory appearing will trigger scanning of it.
m_importAddPending = true;
m_fullReset = false;
}
}
m_importTimerCount = 0;
m_importTimer.start();
return {};
}
void BundleImporter::handleImportTimer()
{
auto handleFailure = [this]() {
m_importTimer.stop();
m_fullReset = false;
m_importAddPending = false;
m_importTimerCount = 0;
m_pendingTypes.clear();
emit importFinished({});
};
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (!model || ++m_importTimerCount > 100) {
handleFailure();
return;
}
if (m_fullReset) {
// Force code model reset to notice changes to existing module
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager)
modelManager->resetCodeModel();
m_fullReset = false;
return;
}
if (m_importAddPending) {
try {
Import import = Import::createLibraryImport(m_moduleName, "1.0");
if (model->possibleImports().contains(import)) {
model->changeImports({import}, {});
m_importAddPending = false;
}
} catch (const RewritingException &) {
// Import adding is unlikely to succeed later, either, so just bail out
handleFailure();
}
return;
}
// Detect when the code model has the new material(s) fully available
const QStringList pendingTypes = m_pendingTypes;
for (const QString &pendingType : pendingTypes) {
NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
if (metaInfo.isValid() && !metaInfo.superClasses().empty()) {
m_pendingTypes.removeAll(pendingType);
emit importFinished(metaInfo);
}
}
if (m_pendingTypes.isEmpty()) {
m_importTimer.stop();
m_importTimerCount = 0;
}
}
} // namespace QmlDesigner::Internal

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** 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 <utils/filepath.h>
#include "nodemetainfo.h"
#include <QTimer>
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
namespace QmlDesigner::Internal {
class BundleImporter : public QObject
{
Q_OBJECT
public:
BundleImporter(const QString &bundleDir,
const QString &bundleId,
const QStringList &sharedFiles,
QObject *parent = nullptr);
~BundleImporter() = default;
QString importComponent(const QString &qmlFile,
const QStringList &files);
signals:
// The metaInfo parameter will be invalid if an error was encountered during
// asynchronous part of the import. In this case all remaining pending imports have been
// terminated, and will not receive separate importFinished notifications.
void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
private:
void handleImportTimer();
Utils::FilePath m_bundleDir;
QString m_bundleId;
QString m_moduleName;
QStringList m_sharedFiles;
QTimer m_importTimer;
int m_importTimerCount = 0;
bool m_importAddPending = false;
bool m_fullReset = false;
QStringList m_pendingTypes;
};
} // namespace QmlDesigner::Internal

View File

@@ -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");
}

View File

@@ -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();
};

View File

@@ -5,12 +5,14 @@
#include "materialeditorqmlbackend.h" #include "materialeditorqmlbackend.h"
#include "materialeditorcontextobject.h" #include "materialeditorcontextobject.h"
#include "materialeditordynamicpropertiesproxymodel.h"
#include "propertyeditorvalue.h" #include "propertyeditorvalue.h"
#include "materialeditortransaction.h" #include "materialeditortransaction.h"
#include "assetslibrarywidget.h" #include "assetslibrarywidget.h"
#include <auxiliarydataproperties.h> #include <auxiliarydataproperties.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <dynamicpropertiesmodel.h>
#include <metainfo.h> #include <metainfo.h>
#include <nodeinstanceview.h> #include <nodeinstanceview.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
@@ -49,6 +51,7 @@ namespace QmlDesigner {
MaterialEditorView::MaterialEditorView(QWidget *parent) MaterialEditorView::MaterialEditorView(QWidget *parent)
: AbstractView(parent) : AbstractView(parent)
, m_stackedWidget(new QStackedWidget(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); m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7), m_stackedWidget);
connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml); connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml);
@@ -71,6 +74,8 @@ MaterialEditorView::MaterialEditorView(QWidget *parent)
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
m_stackedWidget->setMinimumWidth(250); m_stackedWidget->setMinimumWidth(250);
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME);
MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType();
} }
MaterialEditorView::~MaterialEditorView() MaterialEditorView::~MaterialEditorView()
@@ -294,6 +299,29 @@ void MaterialEditorView::currentTimelineChanged(const ModelNode &)
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this)); 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() void MaterialEditorView::delayedResetView()
{ {
// TODO: it seems the delayed reset is not needed. Leaving it commented out for now just in case it // TODO: it seems the delayed reset is not needed. Leaving it commented out for now just in case it
@@ -575,6 +603,11 @@ void MaterialEditorView::setupQmlBackend()
m_qmlBackEnd = currentQmlBackend; m_qmlBackEnd = currentQmlBackend;
if (m_hasMaterialRoot)
m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial);
else
m_dynamicPropertiesModel->reset();
delayedTypeUpdate(); delayedTypeUpdate();
initPreviewData(); initPreviewData();
@@ -746,6 +779,7 @@ void MaterialEditorView::modelAttached(Model *model)
void MaterialEditorView::modelAboutToBeDetached(Model *model) void MaterialEditorView::modelAboutToBeDetached(Model *model)
{ {
AbstractView::modelAboutToBeDetached(model); AbstractView::modelAboutToBeDetached(model);
m_dynamicPropertiesModel->reset();
m_qmlBackEnd->materialEditorTransaction()->end(); m_qmlBackEnd->materialEditorTransaction()->end();
} }
@@ -778,8 +812,9 @@ void MaterialEditorView::variantPropertiesChanged(const QList<VariantProperty> &
bool changed = false; bool changed = false;
for (const VariantProperty &property : propertyList) { for (const VariantProperty &property : propertyList) {
ModelNode node(property.parentModelNode()); ModelNode node(property.parentModelNode());
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) {
if (property.isDynamic())
m_dynamicPropertiesModel->variantPropertyChanged(property);
if (m_selectedMaterial.property(property.name()).isBindingProperty()) if (m_selectedMaterial.property(property.name()).isBindingProperty())
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
else else
@@ -805,6 +840,8 @@ void MaterialEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedMaterial).isAliasExported()); m_qmlBackEnd->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedMaterial).isAliasExported());
if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { 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()) if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty())
setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name()));
else else
@@ -828,6 +865,16 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node,
m_qmlBackEnd->setValueforAuxiliaryProperties(m_selectedMaterial, key); m_qmlBackEnd->setValueforAuxiliaryProperties(m_selectedMaterial, key);
} }
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 // request render image for the selected material node
void MaterialEditorView::requestPreviewRender() void MaterialEditorView::requestPreviewRender()
{ {
@@ -936,7 +983,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN
QTC_ASSERT(material.isValid(), return); QTC_ASSERT(material.isValid(), return);
executeInTransaction("MaterialEditorView:renameMaterial", [&] { executeInTransaction("MaterialEditorView:renameMaterial", [&] {
material.setIdWithRefactoring(generateIdFromName(newName)); material.setIdWithRefactoring(model()->generateIdFromName(newName, "material"));
VariantProperty objNameProp = material.variantProperty("objectName"); VariantProperty objNameProp = material.variantProperty("objectName");
objNameProp.setValue(newName); objNameProp.setValue(newName);
@@ -965,7 +1012,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
// set name and id // set name and id
QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy"; QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy";
duplicateMat.modelNode().variantProperty("objectName").setValue(newName); duplicateMat.modelNode().variantProperty("objectName").setValue(newName);
duplicateMat.modelNode().setIdWithoutRefactoring(generateIdFromName(newName)); duplicateMat.modelNode().setIdWithoutRefactoring(model()->generateIdFromName(newName, "material"));
// sync properties // sync properties
const QList<AbstractProperty> props = material.properties(); const QList<AbstractProperty> props = material.properties();
@@ -991,6 +1038,7 @@ void MaterialEditorView::customNotification([[maybe_unused]] const AbstractView
if (identifier == "selected_material_changed") { if (identifier == "selected_material_changed") {
if (!m_hasMaterialRoot) { if (!m_hasMaterialRoot) {
m_selectedMaterial = nodeList.first(); m_selectedMaterial = nodeList.first();
m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial);
QTimer::singleShot(0, this, &MaterialEditorView::resetView); QTimer::singleShot(0, this, &MaterialEditorView::resetView);
} }
} else if (identifier == "apply_to_selected_triggered") { } else if (identifier == "apply_to_selected_triggered") {
@@ -1072,38 +1120,4 @@ void MaterialEditorView::reloadQml()
resetView(); resetView();
} }
// generate a unique camelCase id from a name
QString MaterialEditorView::generateIdFromName(const QString &name)
{
QString newId;
if (name.isEmpty()) {
newId = "material";
} else {
// convert to camel case
QStringList nameWords = name.split(" ");
nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
for (int i = 1; i < nameWords.size(); ++i)
nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
newId = nameWords.join("");
// if id starts with a number prepend an underscore
if (newId.at(0).isDigit())
newId.prepend('_');
}
QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
while (hasId(newId)) { // id exists
QRegularExpressionMatch match = rgx.match(newId);
if (match.hasMatch()) { // ends with a number, increment it
QString numStr = match.captured();
int num = numStr.toInt() + 1;
newId = newId.mid(0, match.capturedStart()) + QString::number(num);
} else {
newId.append('1');
}
}
return newId;
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -22,6 +22,10 @@ namespace QmlDesigner {
class ModelNode; class ModelNode;
class MaterialEditorQmlBackend; class MaterialEditorQmlBackend;
namespace Internal {
class DynamicPropertiesModel;
}
class MaterialEditorView : public AbstractView class MaterialEditorView : public AbstractView
{ {
Q_OBJECT Q_OBJECT
@@ -46,6 +50,7 @@ public:
void auxiliaryDataChanged(const ModelNode &node, void auxiliaryDataChanged(const ModelNode &node,
AuxiliaryDataKeyView key, AuxiliaryDataKeyView key,
const QVariant &data) override; const QVariant &data) override;
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void resetView(); void resetView();
void currentStateChanged(const ModelNode &node) override; void currentStateChanged(const ModelNode &node) override;
@@ -70,6 +75,10 @@ public:
void currentTimelineChanged(const ModelNode &node) override; void currentTimelineChanged(const ModelNode &node) override;
Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
static MaterialEditorView *instance();
public slots: public slots:
void handleToolBarAction(int action); void handleToolBarAction(int action);
void handlePreviewEnvChanged(const QString &envAndValue); void handlePreviewEnvChanged(const QString &envAndValue);
@@ -85,7 +94,6 @@ private:
void reloadQml(); void reloadQml();
void highlightSupportedProperties(bool highlight = true); void highlightSupportedProperties(bool highlight = true);
QString generateIdFromName(const QString &name);
void requestPreviewRender(); void requestPreviewRender();
void applyMaterialToSelectedModels(const ModelNode &material, bool add = false); void applyMaterialToSelectedModels(const ModelNode &material, bool add = false);
@@ -122,6 +130,7 @@ private:
QPointer<QColorDialog> m_colorDialog; QPointer<QColorDialog> m_colorDialog;
QPointer<ItemLibraryInfo> m_itemLibraryInfo; QPointer<ItemLibraryInfo> m_itemLibraryInfo;
Internal::DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -0,0 +1,392 @@
/****************************************************************************
**
** 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>
#include <QScopeGuard>
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)
{
if (m_lock)
return;
if (m_row < 0)
return;
auto propertiesModel = m_model->dynamicPropertiesModel();
VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row);
if (!Internal::DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName()))
return;
m_lock = true;
auto unlock = qScopeGuard([this] { m_lock = false; });
auto view = propertiesModel->view();
RewriterTransaction transaction = view->beginRewriterTransaction(
QByteArrayLiteral("DynamicPropertiesModel::commitValue"));
try {
if (view->currentState().isBaseState()) {
if (variantProperty.value() != value)
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
} else {
QmlObjectNode objectNode = variantProperty.parentQmlObjectNode();
QTC_CHECK(objectNode.isValid());
PropertyName name = variantProperty.name();
if (objectNode.isValid() && objectNode.modelValue(name) != value)
objectNode.setVariantProperty(name, value);
}
transaction.commit(); //committing in the try block
} catch (Exception &e) {
e.showException();
}
}
void DynamicPropertyRow::commitExpression(const QString &expression)
{
if (m_lock)
return;
if (m_row < 0)
return;
m_lock = true;
auto unlock = qScopeGuard([this] { m_lock = false; });
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();
}

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** 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;
bool m_lock = false;
};
QML_DECLARE_TYPE(DynamicPropertyRow)

View File

@@ -567,6 +567,13 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo &
if (propertyType.isFont() || itemInfo.hasProperty(itemProperty) if (propertyType.isFont() || itemInfo.hasProperty(itemProperty)
|| propertyType.isBasedOn(textInfo, rectangleInfo, imageInfo)) || propertyType.isBasedOn(textInfo, rectangleInfo, imageInfo))
return false; return false;
// TODO In 8.0 this is now the following, which conflicts with master:
// if (typeName == "font" || typeName == "Texture" || typeName == "vector4d"
// || itemInfo.hasProperty(itemProperty)
// || textInfo.isSubclassOf(typeName)
// || rectangleInfo.isSubclassOf(typeName)
// || imageInfo.isSubclassOf(typeName))
// return false;
return true; return true;
} }
@@ -582,10 +589,13 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
QStringList allTypes; // all template types QStringList allTypes; // all template types
QStringList separateSectionTypes; // separate section types only QStringList separateSectionTypes; // separate section types only
QStringList needsTypeArgTypes; // types that need type as third parameter
for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) {
if (node->propertyNames().contains("separateSection")) if (node->propertyNames().contains("separateSection"))
separateSectionTypes.append(variantToStringList(node->property("typeNames").value)); separateSectionTypes.append(variantToStringList(node->property("typeNames").value));
if (node->propertyNames().contains("needsTypeArg"))
needsTypeArgTypes.append(variantToStringList(node->property("typeNames").value));
allTypes.append(variantToStringList(node->property("typeNames").value)); allTypes.append(variantToStringList(node->property("typeNames").value));
} }
@@ -643,8 +653,8 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
Utils::sort(basicProperties, propertyMetaInfoCompare); Utils::sort(basicProperties, propertyMetaInfoCompare);
auto findAndFillTemplate = [&nodes, &node](const PropertyName &label, auto findAndFillTemplate = [&nodes, &node, &needsTypeArgTypes](const PropertyName &label,
const PropertyMetaInfo &property) { const PropertyMetaInfo &property) {
const auto &propertyName = property.name(); const auto &propertyName = property.name();
PropertyName underscoreProperty = propertyName; PropertyName underscoreProperty = propertyName;
underscoreProperty.replace('.', '_'); underscoreProperty.replace('.', '_');
@@ -663,7 +673,14 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
QString source = QString::fromUtf8(file.readAll()); QString source = QString::fromUtf8(file.readAll());
file.close(); file.close();
filledTemplate = source.arg(QString::fromUtf8(label)).arg(QString::fromUtf8(underscoreProperty)); if (needsTypeArgTypes.contains(QString::fromUtf8(typeName))) {
filledTemplate = source.arg(QString::fromUtf8(label),
QString::fromUtf8(underscoreProperty),
QString::fromUtf8(typeName));
} else {
filledTemplate = source.arg(QString::fromUtf8(label),
QString::fromUtf8(underscoreProperty));
}
} else { } else {
qWarning().nospace() << "template definition source file not found:" << fileName; qWarning().nospace() << "template definition source file not found:" << fileName;
} }

View File

@@ -8,6 +8,7 @@
#include "bindingeditor/actioneditor.h" #include "bindingeditor/actioneditor.h"
#include "bindingeditor/bindingeditor.h" #include "bindingeditor/bindingeditor.h"
#include "colorpalettebackend.h" #include "colorpalettebackend.h"
#include "selectiondynamicpropertiesproxymodel.h"
#include "fileresourcesmodel.h" #include "fileresourcesmodel.h"
#include "gradientmodel.h" #include "gradientmodel.h"
#include "gradientpresetcustomlistmodel.h" #include "gradientpresetcustomlistmodel.h"
@@ -53,6 +54,8 @@ void Quick2PropertyEditorView::registerQmlTypes()
Tooltip::registerDeclarativeType(); Tooltip::registerDeclarativeType();
EasingCurveEditor::registerDeclarativeType(); EasingCurveEditor::registerDeclarativeType();
RichTextEditorProxy::registerDeclarativeType(); RichTextEditorProxy::registerDeclarativeType();
SelectionDynamicPropertiesProxyModel::registerDeclarativeType();
DynamicPropertyRow::registerDeclarativeType();
const QString resourcePath = PropertyEditorQmlBackend::propertyEditorResourcesPath(); const QString resourcePath = PropertyEditorQmlBackend::propertyEditorResourcesPath();

View File

@@ -13,6 +13,9 @@
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="4" column="3"> <item row="4" column="3">
<widget class="QLabel" name="label_16"> <widget class="QLabel" name="label_16">
<property name="toolTip">
<string>Number of times the animation runs before it stops.</string>
</property>
<property name="text"> <property name="text">
<string>Loops:</string> <string>Loops:</string>
</property> </property>
@@ -49,6 +52,9 @@
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="QLabel" name="label_15"> <widget class="QLabel" name="label_15">
<property name="toolTip">
<string>Sets the animation to loop indefinitely.</string>
</property>
<property name="text"> <property name="text">
<string>Continuous</string> <string>Continuous</string>
</property> </property>
@@ -113,6 +119,9 @@
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="toolTip">
<string>Name for the animation.</string>
</property>
<property name="text"> <property name="text">
<string>Animation ID:</string> <string>Animation ID:</string>
</property> </property>
@@ -158,6 +167,9 @@
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="QLabel" name="label_23"> <widget class="QLabel" name="label_23">
<property name="toolTip">
<string>State to activate when the animation finishes.</string>
</property>
<property name="text"> <property name="text">
<string>Finished:</string> <string>Finished:</string>
</property> </property>
@@ -165,6 +177,9 @@
</item> </item>
<item row="4" column="7"> <item row="4" column="7">
<widget class="QLabel" name="label_17"> <widget class="QLabel" name="label_17">
<property name="toolTip">
<string>Runs the animation backwards to the beginning when it reaches the end.</string>
</property>
<property name="text"> <property name="text">
<string>Ping pong</string> <string>Ping pong</string>
</property> </property>
@@ -218,6 +233,9 @@
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="toolTip">
<string>Runs the animation automatically when the base state is active.</string>
</property>
<property name="text"> <property name="text">
<string>Running in base state</string> <string>Running in base state</string>
</property> </property>
@@ -253,6 +271,9 @@
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QLabel" name="label_12"> <widget class="QLabel" name="label_12">
<property name="toolTip">
<string>First frame of the animation.</string>
</property>
<property name="text"> <property name="text">
<string>Start frame:</string> <string>Start frame:</string>
</property> </property>
@@ -286,6 +307,9 @@
</item> </item>
<item row="3" column="7"> <item row="3" column="7">
<widget class="QLabel" name="label_14"> <widget class="QLabel" name="label_14">
<property name="toolTip">
<string>Length of the animation in milliseconds. If you set a shorter duration than the number of frames, frames are left out from the end of the animation.</string>
</property>
<property name="text"> <property name="text">
<string>Duration:</string> <string>Duration:</string>
</property> </property>
@@ -293,6 +317,9 @@
</item> </item>
<item row="3" column="3"> <item row="3" column="3">
<widget class="QLabel" name="label_13"> <widget class="QLabel" name="label_13">
<property name="toolTip">
<string>Last frame of the animation.</string>
</property>
<property name="text"> <property name="text">
<string>End frame:</string> <string>End frame:</string>
</property> </property>

View File

@@ -42,6 +42,9 @@
</item> </item>
<item row="2" column="4"> <item row="2" column="4">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="toolTip">
<string>Last frame of the timeline.</string>
</property>
<property name="text"> <property name="text">
<string>End frame:</string> <string>End frame:</string>
</property> </property>
@@ -52,6 +55,9 @@
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label_6">
<property name="toolTip">
<string>First frame of the timeline. Negative numbers are allowed.</string>
</property>
<property name="text"> <property name="text">
<string>Start frame:</string> <string>Start frame:</string>
</property> </property>
@@ -87,6 +93,9 @@
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip">
<string>To create an expression binding animation, delete all animations from this timeline.</string>
</property>
<property name="text"> <property name="text">
<string>Expression binding</string> <string>Expression binding</string>
</property> </property>
@@ -94,6 +103,9 @@
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="toolTip">
<string>Name for the timeline.</string>
</property>
<property name="text"> <property name="text">
<string>Timeline ID:</string> <string>Timeline ID:</string>
</property> </property>
@@ -152,6 +164,9 @@
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="toolTip">
<string>Sets the expression to bind the current keyframe to.</string>
</property>
<property name="text"> <property name="text">
<string>Expression binding:</string> <string>Expression binding:</string>
</property> </property>

View File

@@ -145,7 +145,7 @@ public:
void emitUpdateActiveScene3D(const QVariantMap &sceneState); void emitUpdateActiveScene3D(const QVariantMap &sceneState);
void emitModelNodelPreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); void emitModelNodelPreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
void emitImport3DSupportChanged(const QVariantMap &supportMap); void emitImport3DSupportChanged(const QVariantMap &supportMap);
void emitModelAtPosResult(const ModelNode &modelNode); void emitNodeAtPosResult(const ModelNode &modelNode);
void sendTokenToInstances(const QString &token, int number, const QVector<ModelNode> &nodeVector); void sendTokenToInstances(const QString &token, int number, const QVector<ModelNode> &nodeVector);
@@ -213,7 +213,7 @@ public:
virtual void renderImage3DChanged(const QImage &image); virtual void renderImage3DChanged(const QImage &image);
virtual void updateActiveScene3D(const QVariantMap &sceneState); virtual void updateActiveScene3D(const QVariantMap &sceneState);
virtual void updateImport3DSupport(const QVariantMap &supportMap); virtual void updateImport3DSupport(const QVariantMap &supportMap);
virtual void modelAtPosReady(const ModelNode &modelNode); virtual void nodeAtPosReady(const ModelNode &modelNode);
virtual void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); virtual void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
virtual void dragStarted(QMimeData *mimeData); virtual void dragStarted(QMimeData *mimeData);

View File

@@ -138,8 +138,8 @@ public:
bool hasId(const QString &id) const; bool hasId(const QString &id) const;
bool hasImport(const QString &importUrl) const; bool hasImport(const QString &importUrl) const;
QString generateNewId(const QString &prefixName) const; QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const;
QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const;
void startDrag(QMimeData *mimeData, const QPixmap &icon); void startDrag(QMimeData *mimeData, const QPixmap &icon);
void endDrag(); void endDrag();

View File

@@ -97,6 +97,11 @@ public:
static QVariant instanceValue(const ModelNode &modelNode, const PropertyName &name); static QVariant instanceValue(const ModelNode &modelNode, const PropertyName &name);
static QString generateTranslatableText(const QString& text); static QString generateTranslatableText(const QString& text);
static QString stripedTranslatableTextFunction(const QString &text);
static QString convertToCorrectTranslatableFunction(const QString &text);
QString simplifiedTypeName() const; QString simplifiedTypeName() const;
QStringList allStateNames() const; QStringList allStateNames() const;

View File

@@ -1736,9 +1736,9 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
} else if (command.type() == PuppetToCreatorCommand::Import3DSupport) { } else if (command.type() == PuppetToCreatorCommand::Import3DSupport) {
const QVariantMap supportMap = qvariant_cast<QVariantMap>(command.data()); const QVariantMap supportMap = qvariant_cast<QVariantMap>(command.data());
emitImport3DSupportChanged(supportMap); emitImport3DSupportChanged(supportMap);
} else if (command.type() == PuppetToCreatorCommand::ModelAtPos) { } else if (command.type() == PuppetToCreatorCommand::NodeAtPos) {
ModelNode modelNode = modelNodeForInternalId(command.data().toUInt()); ModelNode modelNode = modelNodeForInternalId(command.data().toUInt());
emitModelAtPosResult(modelNode); emitNodeAtPosResult(modelNode);
} }
} }
@@ -1955,6 +1955,9 @@ void NodeInstanceView::endNanotrace()
QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &modelNode, const ModelNode &renderNode) QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &modelNode, const ModelNode &renderNode)
{ {
if (!modelNode.isValid())
return {};
ModelNodePreviewImageData imageData; ModelNodePreviewImageData imageData;
// We need puppet to generate the image, which needs to be asynchronous. // We need puppet to generate the image, which needs to be asynchronous.

View File

@@ -389,7 +389,7 @@ void AbstractView::updateImport3DSupport(const QVariantMap & /*supportMap*/)
} }
// a Quick3DModel that is picked at the requested position in the 3D Editor // a Quick3DModel that is picked at the requested position in the 3D Editor
void AbstractView::modelAtPosReady(const ModelNode & /*modelNode*/) {} void AbstractView::nodeAtPosReady(const ModelNode & /*modelNode*/) {}
void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, const QPixmap & /*pixmap*/) void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, const QPixmap & /*pixmap*/)
{ {
@@ -781,10 +781,10 @@ void AbstractView::emitImport3DSupportChanged(const QVariantMap &supportMap)
model()->d->notifyImport3DSupportChanged(supportMap); model()->d->notifyImport3DSupportChanged(supportMap);
} }
void AbstractView::emitModelAtPosResult(const ModelNode &modelNode) void AbstractView::emitNodeAtPosResult(const ModelNode &modelNode)
{ {
if (model()) if (model())
model()->d->notifyModelAtPosResult(modelNode); model()->d->notifyNodeAtPosResult(modelNode);
} }
void AbstractView::emitRewriterEndTransaction() void AbstractView::emitRewriterEndTransaction()

View File

@@ -299,9 +299,10 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName,
Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name());
if (internalProperty->isBindingProperty() if (internalProperty->isBindingProperty()
&& internalProperty->toBindingProperty()->expression() == expression && internalProperty->toBindingProperty()->expression() == expression
&& internalProperty->toBindingProperty()->dynamicTypeName() == typeName) && internalProperty->toBindingProperty()->dynamicTypeName() == typeName) {
return; return;
}
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())

View File

@@ -579,9 +579,9 @@ void ModelPrivate::notifyImport3DSupportChanged(const QVariantMap &supportMap)
notifyInstanceChanges([&](AbstractView *view) { view->updateImport3DSupport(supportMap); }); notifyInstanceChanges([&](AbstractView *view) { view->updateImport3DSupport(supportMap); });
} }
void ModelPrivate::notifyModelAtPosResult(const ModelNode &modelNode) void ModelPrivate::notifyNodeAtPosResult(const ModelNode &modelNode)
{ {
notifyInstanceChanges([&](AbstractView *view) { view->modelAtPosReady(modelNode); }); notifyInstanceChanges([&](AbstractView *view) { view->nodeAtPosReady(modelNode); });
} }
void ModelPrivate::notifyDragStarted(QMimeData *mimeData) void ModelPrivate::notifyDragStarted(QMimeData *mimeData)
@@ -1539,6 +1539,41 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP
return newId; return newId;
} }
// Generate a unique camelCase id from a name
// note: this methods does the same as generateNewId(). The 2 methods should be merged into one
QString Model::generateIdFromName(const QString &name, const QString &fallbackId) const
{
QString newId;
if (name.isEmpty()) {
newId = fallbackId;
} else {
// convert to camel case
QStringList nameWords = name.split(" ");
nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
for (int i = 1; i < nameWords.size(); ++i)
nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
newId = nameWords.join("");
// if id starts with a number prepend an underscore
if (newId.at(0).isDigit())
newId.prepend('_');
}
QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
while (hasId(newId)) { // id exists
QRegularExpressionMatch match = rgx.match(newId);
if (match.hasMatch()) { // ends with a number, increment it
QString numStr = match.captured();
int num = numStr.toInt() + 1;
newId = newId.mid(0, match.capturedStart()) + QString::number(num);
} else {
newId.append('1');
}
}
return newId;
}
void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) void Model::startDrag(QMimeData *mimeData, const QPixmap &icon)
{ {
d->notifyDragStarted(mimeData); d->notifyDragStarted(mimeData);
@@ -1562,11 +1597,6 @@ NotNullPointer<const ProjectStorage<Sqlite::Database>> Model::projectStorage() c
return d->projectStorage; return d->projectStorage;
} }
QString Model::generateNewId(const QString &prefixName) const
{
return generateNewId(prefixName, QStringLiteral("element"));
}
bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowHigherVersion) const bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowHigherVersion) const
{ {
if (imports().contains(import)) if (imports().contains(import))

View File

@@ -170,7 +170,7 @@ public:
void notifyUpdateActiveScene3D(const QVariantMap &sceneState); void notifyUpdateActiveScene3D(const QVariantMap &sceneState);
void notifyModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); void notifyModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
void notifyImport3DSupportChanged(const QVariantMap &supportMap); void notifyImport3DSupportChanged(const QVariantMap &supportMap);
void notifyModelAtPosResult(const ModelNode &modelNode); void notifyNodeAtPosResult(const ModelNode &modelNode);
void notifyDragStarted(QMimeData *mimeData); void notifyDragStarted(QMimeData *mimeData);
void notifyDragEnded(); void notifyDragEnded();

View File

@@ -637,7 +637,7 @@ QString QmlObjectNode::generateTranslatableText([[maybe_unused]] const QString &
DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt()) { DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt()) {
case 0: return QString(QStringLiteral("qsTr(\"%1\")")).arg(text); case 0: return QString(QStringLiteral("qsTr(\"%1\")")).arg(text);
case 1: return QString(QStringLiteral("qsTrId(\"%1\")")).arg(text); case 1: return QString(QStringLiteral("qsTrId(\"%1\")")).arg(text);
case 2: return QString(QStringLiteral("qsTranslate(\"\"\"%1\")")).arg(text); case 2: return QString(QStringLiteral("qsTranslate(\"%1\", \"context\")")).arg(text);
default: default:
break; break;
@@ -648,6 +648,21 @@ QString QmlObjectNode::generateTranslatableText([[maybe_unused]] const QString &
#endif #endif
} }
QString QmlObjectNode::stripedTranslatableTextFunction(const QString &text)
{
const QRegularExpression regularExpressionPattern(
QLatin1String("^qsTr(|Id|anslate)\\(\"(.*)\"\\)$"));
const QRegularExpressionMatch match = regularExpressionPattern.match(text);
if (match.hasMatch())
return match.captured(2);
return text;
}
QString QmlObjectNode::convertToCorrectTranslatableFunction(const QString &text)
{
return generateTranslatableText(stripedTranslatableTextFunction(text));
}
TypeName QmlObjectNode::instanceType(const PropertyName &name) const TypeName QmlObjectNode::instanceType(const PropertyName &name) const
{ {
return nodeInstance().instanceType(name); return nodeInstance().instanceType(name);

View File

@@ -274,7 +274,8 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
for (const auto &property : itemLibraryEntry.properties()) { for (const auto &property : itemLibraryEntry.properties()) {
if (property.type() == "binding") { if (property.type() == "binding") {
propertyBindingList.append(PropertyBindingEntry(property.name(), property.value().toString())); const QString value = QmlObjectNode::convertToCorrectTranslatableFunction(property.value().toString());
propertyBindingList.append(PropertyBindingEntry(property.name(), value));
} else if (property.type() == "enum") { } else if (property.type() == "enum") {
propertyEnumList.append(PropertyBindingEntry(property.name(), property.value().toString())); propertyEnumList.append(PropertyBindingEntry(property.name(), property.value().toString()));
} else if (property.value().toString() == QString::fromLatin1(imagePlaceHolder)) { } else if (property.value().toString() == QString::fromLatin1(imagePlaceHolder)) {

View File

@@ -62,6 +62,7 @@ void DesignerSettings::fromSettings(QSettings *settings)
restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, defaultValue); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, defaultValue);
restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa"); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa");
restoreValue(settings, DesignerSettingsKey::SMOOTH_RENDERING, false); restoreValue(settings, DesignerSettingsKey::SMOOTH_RENDERING, false);
restoreValue(settings, DesignerSettingsKey::SHOW_DEBUG_SETTINGS, false);
settings->endGroup(); settings->endGroup();
settings->endGroup(); settings->endGroup();

View File

@@ -61,6 +61,7 @@ const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundC
const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/";
const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles";
const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets";
const char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; const char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon";
const char QUICK_3D_ASSET_ICON_DIR[] = "_icons"; const char QUICK_3D_ASSET_ICON_DIR[] = "_icons";

View File

@@ -733,8 +733,12 @@ Project {
"materialbrowser/materialbrowserview.h", "materialbrowser/materialbrowserview.h",
"materialbrowser/materialbrowserwidget.cpp", "materialbrowser/materialbrowserwidget.cpp",
"materialbrowser/materialbrowserwidget.h", "materialbrowser/materialbrowserwidget.h",
"materialbrowser/bundleimporter.cpp",
"materialbrowser/bundleimporter.h",
"materialeditor/materialeditorcontextobject.cpp", "materialeditor/materialeditorcontextobject.cpp",
"materialeditor/materialeditorcontextobject.h", "materialeditor/materialeditorcontextobject.h",
"materialeditor/materialeditordynamicpropertiesproxymodel.cpp",
"materialeditor/materialeditordynamicpropertiesproxymodel.h",
"materialeditor/materialeditorqmlbackend.cpp", "materialeditor/materialeditorqmlbackend.cpp",
"materialeditor/materialeditorqmlbackend.h", "materialeditor/materialeditorqmlbackend.h",
"materialeditor/materialeditortransaction.cpp", "materialeditor/materialeditortransaction.cpp",
@@ -769,6 +773,8 @@ Project {
"propertyeditor/colorpalettebackend.h", "propertyeditor/colorpalettebackend.h",
"propertyeditor/designerpropertymap.cpp", "propertyeditor/designerpropertymap.cpp",
"propertyeditor/designerpropertymap.h", "propertyeditor/designerpropertymap.h",
"propertyeditor/dynamicpropertiesproxymodel.cpp",
"propertyeditor/dynamicpropertiesproxymodel.h",
"propertyeditor/fileresourcesmodel.cpp", "propertyeditor/fileresourcesmodel.cpp",
"propertyeditor/fileresourcesmodel.h", "propertyeditor/fileresourcesmodel.h",
"propertyeditor/itemfiltermodel.cpp", "propertyeditor/itemfiltermodel.cpp",
@@ -887,6 +893,8 @@ Project {
"connectioneditor/connectionviewwidget.ui", "connectioneditor/connectionviewwidget.ui",
"connectioneditor/dynamicpropertiesmodel.cpp", "connectioneditor/dynamicpropertiesmodel.cpp",
"connectioneditor/dynamicpropertiesmodel.h", "connectioneditor/dynamicpropertiesmodel.h",
"connectioneditor/selectiondynamicpropertiesproxymodel.cpp",
"connectioneditor/selectiondynamicpropertiesproxymodel.h",
"connectioneditor/stylesheet.css", "connectioneditor/stylesheet.css",
"curveeditor/curveeditorview.cpp", "curveeditor/curveeditorview.cpp",
"curveeditor/curveeditorview.h", "curveeditor/curveeditorview.h",

View File

@@ -666,6 +666,8 @@ void ExampleSetModel::selectExampleSet(int index)
if (getType(m_selectedExampleSetIndex) == ExampleSetModel::QtExampleSet) { if (getType(m_selectedExampleSetIndex) == ExampleSetModel::QtExampleSet) {
QtVersion *selectedQtVersion = QtVersionManager::version(getQtId(m_selectedExampleSetIndex)); QtVersion *selectedQtVersion = QtVersionManager::version(getQtId(m_selectedExampleSetIndex));
m_selectedQtTypes = selectedQtVersion->targetDeviceTypes(); m_selectedQtTypes = selectedQtVersion->targetDeviceTypes();
} else {
m_selectedQtTypes.clear();
} }
emit selectedExampleSetChanged(m_selectedExampleSetIndex); emit selectedExampleSetChanged(m_selectedExampleSetIndex);
} }

View File

@@ -63,7 +63,7 @@ GenericDirectUploadService::GenericDirectUploadService(QObject *parent)
{ {
connect(&d->uploader, &FileTransfer::done, this, [this](const ProcessResultData &result) { connect(&d->uploader, &FileTransfer::done, this, [this](const ProcessResultData &result) {
QTC_ASSERT(d->state == Uploading, return); QTC_ASSERT(d->state == Uploading, return);
if (result.m_error != QProcess::UnknownError) { if (result.m_error != QProcess::UnknownError || result.m_exitCode != 0) {
emit errorMessage(result.m_errorString); emit errorMessage(result.m_errorString);
setFinished(); setFinished();
handleDeploymentDone(); handleDeploymentDone();

View File

@@ -7851,7 +7851,7 @@ void TextEditorWidget::dropEvent(QDropEvent *e)
MultiTextCursor cursor = multiTextCursor(); MultiTextCursor cursor = multiTextCursor();
cursor.beginEditBlock(); cursor.beginEditBlock();
const QTextCursor eventCursor = cursorForPosition(e->pos()); const QTextCursor eventCursor = cursorForPosition(e->pos());
if (e->dropAction() == Qt::MoveAction) if (e->dropAction() == Qt::MoveAction && e->source() == viewport())
cursor.removeSelectedText(); cursor.removeSelectedText();
cursor.setCursors({eventCursor}); cursor.setCursors({eventCursor});
setMultiTextCursor(cursor); setMultiTextCursor(cursor);
@@ -7864,6 +7864,7 @@ void TextEditorWidget::dropEvent(QDropEvent *e)
insertFromMimeData(mime); insertFromMimeData(mime);
delete mimeOverwrite; delete mimeOverwrite;
cursor.endEditBlock(); cursor.endEditBlock();
e->acceptProposedAction();
} }
QMimeData *TextEditorWidget::duplicateMimeData(const QMimeData *source) QMimeData *TextEditorWidget::duplicateMimeData(const QMimeData *source)

View File

@@ -406,7 +406,7 @@ void VcsOutputWindow::append(const QString &text, MessageStyle style, bool silen
void VcsOutputWindow::appendError(const QString &text) void VcsOutputWindow::appendError(const QString &text)
{ {
append(text.endsWith('\n') ? text : text + '\n', Error, false); append((text.endsWith('\n') || text.endsWith('\r')) ? text : text + '\n', Error, false);
} }
void VcsOutputWindow::appendWarning(const QString &text) void VcsOutputWindow::appendWarning(const QString &text)

Binary file not shown.

Binary file not shown.

Binary file not shown.