forked from qt-creator/qt-creator
QmlDesigner: Add QML front-end for ConnectionView
This is still disabled by default until the UI gets polished. Add roles to models. ConnectionModel, BindingModel and DynamicPropertiesModel. In QML roles are used in the ListView to "mimic" columns. The columns are only relevant for the "old" cpp QTableView and can be removed. Making currentIndex part of the model. If rows are removed and added again we have to "reset/keep" the currentIndex as part of the model, because the index is temporarly invalid on the view. Implementing DynamicPropertiesModelBackendDelegate as reference. This is a pure "backend delegate" that exposes all relevant properties for a current row to QML. Adding StudioQuickWidget and exposing the backend. Adding StudioQmlTextBackend and StudioQmlComboBoxBackend to StudioQuickWidget (should be moved into their own files). Those helper classes make is easy to expose a property to a combobox or textfield. The backend has to know nothing about the actual frontend and those classes act as a mini-model for a view in QML. The API is similar to UI controls. Change-Id: I7a2c6ad951306fbca1d586fb8f278acdd91a064b Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io> Reviewed-by: Knud Dollereder <knud.dollereder@qt.io>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
BindingsDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
|
||||
property var backend
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text111
|
||||
x: 80
|
||||
y: 25
|
||||
color: "red"
|
||||
text: backend.targetNode
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 101
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -335
|
||||
model: backend.property.model ?? []
|
||||
enabled: false
|
||||
//I see no use case to actually change the property name
|
||||
//onActivated: backend.targetNode.activateIndex(target.currentIndex)
|
||||
property int currentTypeIndex: backend.property.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 13
|
||||
y: 111
|
||||
color: "#ffffff"
|
||||
text: qsTr("Source Propety")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: sourceNode
|
||||
x: 135
|
||||
y: 98
|
||||
width: 156
|
||||
|
||||
model: backend.sourceNode.model ?? []
|
||||
|
||||
onModelChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex
|
||||
|
||||
onActivated: backend.sourceNode.activateIndex(sourceNode.currentIndex)
|
||||
property int currentTypeIndex: backend.sourceNode.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 13
|
||||
y: 88
|
||||
color: "#ffffff"
|
||||
text: qsTr("Source Node")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: sourceProperty
|
||||
x: 140
|
||||
y: 121
|
||||
width: 156
|
||||
|
||||
model: backend.sourceProperty.model ?? []
|
||||
onModelChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex
|
||||
onActivated: backend.sourceProperty.activateIndex(
|
||||
sourceProperty.currentIndex)
|
||||
property int currentTypeIndex: backend.sourceProperty.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text3
|
||||
x: 10
|
||||
y: 55
|
||||
color: "#ffffff"
|
||||
text: qsTr("Property")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/* Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
dialog.backend.currentRow = listView.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
BindingsDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
backend: listView.model.delegate
|
||||
}
|
||||
]
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.backend.currentRow = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: targetProperty ?? ""
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: source ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: sourceProperty ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
ConnectionsDialogForm {
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,242 @@
|
||||
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target:")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 95
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -367
|
||||
model: ["mySpinbox", "foo", "backendObject"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 10
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Signal")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: signal
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -207
|
||||
model: ["onClicked", "onPressed", "onReleased"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: action
|
||||
x: 207
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -207
|
||||
model: ["Call Function", "Assign", "ChnageState"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text3
|
||||
x: 207
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Action")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Item {
|
||||
id: functionGroup
|
||||
x: 0
|
||||
y: 276
|
||||
width: 400
|
||||
height: 176
|
||||
|
||||
Text {
|
||||
id: text4
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: functionTarget
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -48
|
||||
model: ["mySpinBox", "backendObject", "someButton"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: functionName
|
||||
x: 203
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -48
|
||||
model: ["start", "trigger", "stop"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text5
|
||||
x: 203
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("Function")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: statesGroup
|
||||
x: 0
|
||||
y: 383
|
||||
width: 400
|
||||
height: 106
|
||||
Text {
|
||||
id: text6
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("State Group")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: stateGroup
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["default", "State Group 01", "State Group 02"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: stateName
|
||||
x: 209
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["State 01", "State 02", "State 03"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text7
|
||||
x: 209
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("State")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: assignment
|
||||
x: 10
|
||||
y: 505
|
||||
width: 400
|
||||
height: 106
|
||||
Text {
|
||||
id: text8
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueTarget
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["mySpinBox", "myButton", "backendObject"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueSource
|
||||
x: 209
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["mySpinBox", "myButton", "backendObject"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text9
|
||||
x: 209
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("source")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text10
|
||||
x: 17
|
||||
y: 76
|
||||
color: "#ffffff"
|
||||
text: qsTr("value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueOut
|
||||
x: 10
|
||||
y: -2
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 84
|
||||
model: ["width", "height", "opacity"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueIn
|
||||
x: 209
|
||||
y: -2
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 84
|
||||
model: ["width", "height", "x", "y"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text11
|
||||
x: 209
|
||||
y: 76
|
||||
color: "#ffffff"
|
||||
text: qsTr("value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/*
|
||||
Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
ConnectionsDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: signal
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: action
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
117
share/qtcreator/qmldesigner/connectionseditor/Main.qml
Normal file
117
share/qtcreator/qmldesigner/connectionseditor/Main.qml
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
Rectangle {
|
||||
width: 640
|
||||
height: 1080
|
||||
color: "#232222"
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
x: 10
|
||||
y: 10
|
||||
width: 620
|
||||
height: 97
|
||||
color: "#333333"
|
||||
|
||||
Rectangle {
|
||||
id: rectangle1
|
||||
x: 10
|
||||
y: 10
|
||||
width: 600
|
||||
height: 34
|
||||
color: "#00ffffff"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 10
|
||||
color: "#b5b2b2"
|
||||
text: qsTr("Search")
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
x: 10
|
||||
y: 50
|
||||
TabCheckButton {
|
||||
id: connections
|
||||
text: "Connections"
|
||||
checked: true
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
|
||||
TabCheckButton {
|
||||
id: bindings
|
||||
text: "Bindings"
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
|
||||
TabCheckButton {
|
||||
id: properties
|
||||
text: "Properties"
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 577
|
||||
y: 58
|
||||
color: "#ffffff"
|
||||
text: qsTr("+")
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate)
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type)
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type.model)
|
||||
|
||||
if (connections.checked)
|
||||
ConnectionsEditorEditorBackend.connectionModel.add()
|
||||
else if (bindings.checked)
|
||||
ConnectionsEditorEditorBackend.bindingModel.add()
|
||||
else if (properties.checked)
|
||||
ConnectionsEditorEditorBackend.dynamicPropertiesModel.add()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionsListView {
|
||||
visible: connections.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.connectionModel
|
||||
}
|
||||
|
||||
BindingsListView {
|
||||
visible: bindings.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.bindingModel
|
||||
}
|
||||
|
||||
PropertiesListView {
|
||||
visible: properties.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.dynamicPropertiesModel
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Window {
|
||||
id: window
|
||||
width: 400
|
||||
height: 800
|
||||
visible: true
|
||||
flags: Qt.FramelessWindowHint || Qt.Dialog
|
||||
|
||||
color: Qt.transparent
|
||||
|
||||
property int bw: 5
|
||||
|
||||
function popup(item) {
|
||||
print("popup " + item)
|
||||
var padding = 12
|
||||
var p = item.mapToGlobal(0, 0)
|
||||
dialog.x = p.x - dialog.width - padding
|
||||
if (dialog.x < 0)
|
||||
dialog.x = p.x + item.width + padding
|
||||
dialog.y = p.y
|
||||
dialog.show()
|
||||
dialog.raise()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rectangle1
|
||||
color: "#d7d7d7"
|
||||
border.color: "#232323"
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
height: 32
|
||||
color: "#797979"
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
DragHandler {
|
||||
grabPermissions: TapHandler.CanTakeOverFromAnything
|
||||
onActiveChanged: if (active) { window.startSystemMove(); }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rectangle2
|
||||
x: 329
|
||||
width: 16
|
||||
height: 16
|
||||
color: "#ffffff"
|
||||
radius: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
PropertiesDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
property var backend
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Type:")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 95
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -367
|
||||
model: backend.type.model ?? []
|
||||
onActivated: backend.type.activateIndex(target.currentIndex)
|
||||
property int currentTypeIndex: backend.type.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 10
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Name")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: name
|
||||
x: 70
|
||||
y: 131
|
||||
color: "white"
|
||||
width: 156
|
||||
text: backend.name.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.name.activateText(name.text)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 10
|
||||
y: 81
|
||||
color: "#ffffff"
|
||||
text: qsTr("Value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: value
|
||||
color: "red"
|
||||
x: 70
|
||||
y: 81
|
||||
width: 156
|
||||
text: backend.value.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.value.activateText(value.text)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/* Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
dialog.backend.currentRow = listView.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
PropertiesDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
backend: listView.model.delegate
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
|
||||
Connections {
|
||||
target: mouseArea
|
||||
function onClicked() {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.backend.currentRow = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: name ?? ""
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: type ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: value ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(
|
||||
buttonBackground ? buttonBackground.implicitWidth : 0,
|
||||
textItem.implicitWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(
|
||||
buttonBackground ? buttonBackground.implicitHeight : 0,
|
||||
textItem.implicitHeight + topPadding + bottomPadding)
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
text: "My Button"
|
||||
|
||||
background: buttonBackground
|
||||
Rectangle {
|
||||
id: buttonBackground
|
||||
color: "#047eff"
|
||||
implicitWidth: 100
|
||||
implicitHeight: 40
|
||||
opacity: enabled ? 1 : 0.3
|
||||
radius: 12
|
||||
border.color: "#047eff"
|
||||
}
|
||||
|
||||
contentItem: textItem
|
||||
Text {
|
||||
id: textItem
|
||||
text: control.text
|
||||
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
color: "#ffffff"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "normal"
|
||||
when: !control.down && !control.checked
|
||||
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
visible: false
|
||||
color: "#00000000"
|
||||
border.color: "#047eff"
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: "#ffffff"
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "down"
|
||||
when: control.down && !control.checked
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: "#ffffff"
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
color: "#047eff"
|
||||
border.color: "#00000000"
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "down1"
|
||||
when: control.checked
|
||||
extend: "down"
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 Singleton
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int width: 1920
|
||||
readonly property int height: 1080
|
||||
|
||||
property int thumbnailSize: 250
|
||||
// Breakpoint to control when the state thumbnail view toggles from normal to tiny
|
||||
readonly property int thumbnailBreak: 150
|
||||
|
||||
readonly property int minThumbSize: 100
|
||||
readonly property int maxThumbSize: 350
|
||||
}
|
@@ -0,0 +1 @@
|
||||
singleton Constants 1.0 Constants.qml
|
@@ -13,14 +13,16 @@
|
||||
#include <rewritertransaction.h>
|
||||
#include <rewriterview.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
BindingModel::BindingModel(ConnectionView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_connectionView(parent)
|
||||
: QStandardItemModel(parent), m_connectionView(parent),
|
||||
m_delegate(new BindingModelBackendDelegate(this))
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged);
|
||||
}
|
||||
@@ -40,6 +42,31 @@ void BindingModel::resetModel()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void BindingModel::add()
|
||||
{
|
||||
addBindingForCurrentNode();
|
||||
}
|
||||
|
||||
void BindingModel::remove(int row)
|
||||
{
|
||||
deleteBindindByRow(row);
|
||||
}
|
||||
|
||||
int BindingModel::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void BindingModel::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
@@ -232,6 +259,19 @@ void BindingModel::addBindingForCurrentNode()
|
||||
}
|
||||
}
|
||||
|
||||
static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property)
|
||||
{
|
||||
item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole);
|
||||
item->setData(property.name(), BindingModel::TargetPropertyNameRole);
|
||||
|
||||
const AbstractProperty source = property.resolveToProperty();
|
||||
|
||||
if (source.isValid()) {
|
||||
item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole);
|
||||
item->setData(source.name(), BindingModel::SourcePropertyNameRole);
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::addBindingProperty(const BindingProperty &property)
|
||||
{
|
||||
QStandardItem *idItem;
|
||||
@@ -248,6 +288,7 @@ void BindingModel::addBindingProperty(const BindingProperty &property)
|
||||
QList<QStandardItem*> items;
|
||||
|
||||
items.append(idItem);
|
||||
updateDisplayRoles(idItem, property);
|
||||
items.append(targetPropertyNameItem);
|
||||
|
||||
QString sourceNodeName;
|
||||
@@ -267,6 +308,10 @@ void BindingModel::updateBindingProperty(int rowNumber)
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
QStandardItem *idItem = item(rowNumber, 0);
|
||||
if (idItem)
|
||||
updateDisplayRoles(idItem, bindingProperty);
|
||||
|
||||
QString targetPropertyName = QString::fromUtf8(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
|
||||
QString sourceNodeName;
|
||||
@@ -355,6 +400,7 @@ void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &
|
||||
{
|
||||
item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(bindingProperty.name(), Qt::UserRole + 2);
|
||||
updateDisplayRoles(item, bindingProperty);
|
||||
}
|
||||
|
||||
int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
|
||||
@@ -369,6 +415,8 @@ int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
|
||||
|
||||
bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
|
||||
{
|
||||
//TODO reimplement using existing helper functions
|
||||
|
||||
//### todo we assume no expressions yet
|
||||
|
||||
const QString expression = bindingProperty.expression();
|
||||
@@ -438,4 +486,159 @@ void BindingModel::handleException()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> BindingModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"},
|
||||
{TargetPropertyNameRole, "targetProperty"},
|
||||
{SourceNameRole, "source"},
|
||||
{SourcePropertyNameRole, "sourceProperty"}};
|
||||
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
BindingModelBackendDelegate *BindingModel::delegate() const
|
||||
{
|
||||
return m_delegate;
|
||||
}
|
||||
|
||||
BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent)
|
||||
{
|
||||
connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||
handleSourceNodeChanged();
|
||||
});
|
||||
|
||||
connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||
handleSourcePropertyChanged();
|
||||
});
|
||||
}
|
||||
|
||||
int BindingModelBackendDelegate::currentRow() const
|
||||
{
|
||||
return m_currentRow;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::setCurrentRow(int i)
|
||||
{
|
||||
// See BindingDelegate::createEditor
|
||||
|
||||
if (m_currentRow == i)
|
||||
return;
|
||||
|
||||
m_currentRow = i;
|
||||
|
||||
//setup
|
||||
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
QString idLabel = bindingProperty.parentModelNode().id();
|
||||
if (idLabel.isEmpty())
|
||||
idLabel = bindingProperty.parentModelNode().simplifiedTypeName();
|
||||
|
||||
m_targetNode = idLabel;
|
||||
|
||||
emit targetNodeChanged();
|
||||
|
||||
m_property.setModel(model->possibleTargetProperties(bindingProperty));
|
||||
m_property.setCurrentText(QString::fromUtf8(bindingProperty.name()));
|
||||
|
||||
QStringList sourceNodes;
|
||||
|
||||
for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) {
|
||||
if (!modelNode.id().isEmpty())
|
||||
sourceNodes.append(modelNode.id());
|
||||
}
|
||||
|
||||
std::sort(sourceNodes.begin(), sourceNodes.end());
|
||||
m_sourceNode.setModel(sourceNodes);
|
||||
|
||||
QString sourceNodeName;
|
||||
QString sourcePropertyName;
|
||||
model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
|
||||
|
||||
m_sourceNode.setCurrentText(sourceNodeName);
|
||||
|
||||
m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty));
|
||||
m_sourceNodeProperty.setCurrentText(sourcePropertyName);
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
//reset
|
||||
}
|
||||
|
||||
QString BindingModelBackendDelegate::targetNode() const
|
||||
{
|
||||
return m_targetNode;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::property()
|
||||
{
|
||||
return &m_property;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceNode()
|
||||
{
|
||||
return &m_sourceNode;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
|
||||
{
|
||||
return &m_sourceNodeProperty;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleSourceNodeChanged()
|
||||
{
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->connectionView(), return );
|
||||
|
||||
const QString sourceNode = m_sourceNode.currentText();
|
||||
const QString sourceProperty = m_sourceNodeProperty.currentText();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
|
||||
[&bindingProperty, expression]() {
|
||||
bindingProperty.setExpression(
|
||||
expression.trimmed());
|
||||
});
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleSourcePropertyChanged()
|
||||
{
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->connectionView(), return );
|
||||
|
||||
const QString sourceNode = m_sourceNode.currentText();
|
||||
const QString sourceProperty = m_sourceNodeProperty.currentText();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
|
||||
[&bindingProperty, expression]() {
|
||||
bindingProperty.setExpression(
|
||||
expression.trimmed());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -7,16 +7,22 @@
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ConnectionView;
|
||||
class BindingModelBackendDelegate;
|
||||
|
||||
class BindingModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
public:
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
@@ -24,6 +30,15 @@ public:
|
||||
SourceModelNodeRow = 2,
|
||||
SourcePropertyNameRow = 3
|
||||
};
|
||||
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 2,
|
||||
TargetNameRole,
|
||||
TargetPropertyNameRole,
|
||||
SourceNameRole,
|
||||
SourcePropertyNameRole
|
||||
};
|
||||
|
||||
BindingModel(ConnectionView *parent = nullptr);
|
||||
void bindingChanged(const BindingProperty &bindingProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
@@ -37,6 +52,18 @@ public:
|
||||
void addBindingForCurrentNode();
|
||||
void resetModel();
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty,
|
||||
QString *sourceNode,
|
||||
QString *sourceProperty);
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
protected:
|
||||
void addBindingProperty(const BindingProperty &property);
|
||||
void updateBindingProperty(int rowNumber);
|
||||
@@ -46,11 +73,11 @@ protected:
|
||||
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
|
||||
void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
|
||||
int findRowForBinding(const BindingProperty &bindingProperty);
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
BindingModelBackendDelegate *delegate() const;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
@@ -60,7 +87,48 @@ private:
|
||||
bool m_lock = false;
|
||||
bool m_handleDataChanged = false;
|
||||
QString m_exceptionError;
|
||||
int m_currentIndex = 0;
|
||||
BindingModelBackendDelegate *m_delegate = nullptr;
|
||||
};
|
||||
|
||||
class BindingModelBackendDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
|
||||
|
||||
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
|
||||
|
||||
public:
|
||||
BindingModelBackendDelegate(BindingModel *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
//void nameChanged();
|
||||
void targetNodeChanged();
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleException();
|
||||
QString targetNode() const;
|
||||
|
||||
StudioQmlComboBoxBackend *property();
|
||||
StudioQmlComboBoxBackend *sourceNode();
|
||||
StudioQmlComboBoxBackend *sourceProperty();
|
||||
|
||||
void handleSourceNodeChanged();
|
||||
void handleSourcePropertyChanged();
|
||||
|
||||
StudioQmlComboBoxBackend m_property;
|
||||
StudioQmlComboBoxBackend m_sourceNode;
|
||||
StudioQmlComboBoxBackend m_sourceNodeProperty;
|
||||
QString m_exceptionError;
|
||||
int m_currentRow = -1;
|
||||
QString m_targetNode;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -252,6 +252,19 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP
|
||||
{
|
||||
item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
|
||||
item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole);
|
||||
item->setData(signalHandlerProperty.parentModelNode()
|
||||
.bindingProperty("target")
|
||||
.resolveToModelNode()
|
||||
.id(),
|
||||
UserRoles::TargetNameRole);
|
||||
|
||||
// TODO signalHandlerProperty.source() contains a statement that defines the type.
|
||||
// foo.bar() <- function call
|
||||
// foo.state = "literal" //state change
|
||||
//anything else is assignment
|
||||
// e.g. foo.bal = foo2.bula ; foo.bal = "literal" ; goo.gal = true
|
||||
|
||||
item->setData("Assignment", UserRoles::ActionTypeRole);
|
||||
}
|
||||
|
||||
ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const
|
||||
@@ -370,6 +383,16 @@ void ConnectionModel::removeRowFromTable(const SignalHandlerProperty &property)
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionModel::add()
|
||||
{
|
||||
addConnection();
|
||||
}
|
||||
|
||||
void ConnectionModel::remove(int row)
|
||||
{
|
||||
deleteConnectionByRow(row);
|
||||
}
|
||||
|
||||
void ConnectionModel::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
@@ -522,4 +545,12 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co
|
||||
return stringList;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ConnectionModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetPropertyNameRole, "signal"},
|
||||
{TargetNameRole, "target"},
|
||||
{ActionTypeRole, "action"}};
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -26,7 +26,9 @@ public:
|
||||
};
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 1,
|
||||
TargetPropertyNameRole
|
||||
TargetPropertyNameRole,
|
||||
TargetNameRole,
|
||||
ActionTypeRole
|
||||
};
|
||||
ConnectionModel(ConnectionView *parent = nullptr);
|
||||
|
||||
@@ -49,6 +51,9 @@ public:
|
||||
void deleteConnectionByRow(int currentRow);
|
||||
void removeRowFromTable(const SignalHandlerProperty &property);
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
protected:
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
void addConnection(const ModelNode &modelNode);
|
||||
@@ -61,6 +66,8 @@ protected:
|
||||
void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
|
||||
QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "bindingmodel.h"
|
||||
#include "connectionmodel.h"
|
||||
#include "dynamicpropertiesmodel.h"
|
||||
#include "theme.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
@@ -16,19 +17,116 @@
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <viewmanager.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagebox.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QShortcut>
|
||||
#include <QTableView>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
static QString propertyEditorResourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||
}
|
||||
|
||||
class ConnectionViewQuickWidget : public StudioQuickWidget
|
||||
{
|
||||
// Q_OBJECT carefull
|
||||
|
||||
public:
|
||||
ConnectionViewQuickWidget(ConnectionView *connectionEditorView)
|
||||
: m_connectionEditorView(connectionEditorView)
|
||||
|
||||
{
|
||||
engine()->addImportPath(qmlSourcesPath());
|
||||
engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||
engine()->addImportPath(qmlSourcesPath() + "/imports");
|
||||
|
||||
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F12), this);
|
||||
connect(m_qmlSourceUpdateShortcut,
|
||||
&QShortcut::activated,
|
||||
this,
|
||||
&ConnectionViewQuickWidget::reloadQmlSource);
|
||||
|
||||
//setObjectName(Constants::OBJECT_NAME_STATES_EDITOR);
|
||||
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
auto map = registerPropertyMap("ConnectionsEditorEditorBackend");
|
||||
qmlRegisterAnonymousType<DynamicPropertiesModel>("ConnectionsEditorEditorBackend", 1);
|
||||
qmlRegisterAnonymousType<DynamicPropertiesModelBackendDelegate>(
|
||||
"ConnectionsEditorEditorBackend", 1);
|
||||
|
||||
map->setProperties(
|
||||
{{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"dynamicPropertiesModel",
|
||||
QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}});
|
||||
|
||||
Theme::setupTheme(engine());
|
||||
|
||||
setMinimumWidth(195);
|
||||
setMinimumHeight(195);
|
||||
|
||||
// init the first load of the QML UI elements
|
||||
reloadQmlSource();
|
||||
}
|
||||
~ConnectionViewQuickWidget() = default;
|
||||
|
||||
static QString qmlSourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/connectionseditor";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/connectionseditor").toString();
|
||||
}
|
||||
|
||||
private:
|
||||
void reloadQmlSource()
|
||||
{
|
||||
QString connectionEditorQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml");
|
||||
QTC_ASSERT(QFileInfo::exists(connectionEditorQmlFilePath), return );
|
||||
setSource(QUrl::fromLocalFile(connectionEditorQmlFilePath));
|
||||
|
||||
if (!rootObject()) {
|
||||
QString errorString;
|
||||
for (const QQmlError &error : errors())
|
||||
errorString += "\n" + error.toString();
|
||||
|
||||
Core::AsynchronousMessageBox::warning(
|
||||
tr("Cannot Create QtQuick View"),
|
||||
tr("ConnectionsEditorWidget: %1 cannot be created.%2")
|
||||
.arg(qmlSourcesPath(), errorString));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QPointer<ConnectionView> m_connectionEditorView;
|
||||
QShortcut *m_qmlSourceUpdateShortcut;
|
||||
};
|
||||
|
||||
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
|
||||
: AbstractView{externalDependencies}
|
||||
, m_connectionViewWidget(new ConnectionViewWidget())
|
||||
, m_connectionModel(new ConnectionModel(this))
|
||||
, m_bindingModel(new BindingModel(this))
|
||||
, m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this))
|
||||
, m_backendModel(new BackendModel(this))
|
||||
: AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()),
|
||||
m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)),
|
||||
m_backendModel(new BackendModel(this)),
|
||||
m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
|
||||
{
|
||||
connectionViewWidget()->setBindingModel(m_bindingModel);
|
||||
connectionViewWidget()->setConnectionModel(m_connectionModel);
|
||||
@@ -36,8 +134,11 @@ ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependenci
|
||||
connectionViewWidget()->setBackendModel(m_backendModel);
|
||||
}
|
||||
|
||||
ConnectionView::~ConnectionView() = default;
|
||||
|
||||
ConnectionView::~ConnectionView()
|
||||
{
|
||||
// Ensure that QML is deleted first to avoid calling back to C++.
|
||||
delete m_connectionViewQuickWidget.data();
|
||||
}
|
||||
void ConnectionView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
@@ -195,7 +296,14 @@ void ConnectionView::currentStateChanged(const ModelNode &)
|
||||
|
||||
WidgetInfo ConnectionView::widgetInfo()
|
||||
{
|
||||
return createWidgetInfo(m_connectionViewWidget.data(),
|
||||
/* Enable new connection editor here */
|
||||
const bool newEditor = false;
|
||||
|
||||
QWidget *widget = m_connectionViewWidget.data();
|
||||
if (newEditor)
|
||||
widget = m_connectionViewQuickWidget.data();
|
||||
|
||||
return createWidgetInfo(widget,
|
||||
QLatin1String("ConnectionView"),
|
||||
WidgetInfo::LeftPane,
|
||||
0,
|
||||
@@ -257,6 +365,20 @@ BackendModel *ConnectionView::backendModel() const
|
||||
return m_backendModel;
|
||||
}
|
||||
|
||||
int ConnectionView::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void ConnectionView::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
ConnectionView *ConnectionView::instance()
|
||||
{
|
||||
|
||||
|
@@ -20,11 +20,14 @@ class BindingModel;
|
||||
class ConnectionModel;
|
||||
class DynamicPropertiesModel;
|
||||
class BackendModel;
|
||||
class ConnectionViewQuickWidget;
|
||||
|
||||
class ConnectionView : public AbstractView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
|
||||
public:
|
||||
ConnectionView(ExternalDependenciesInterface &externalDependencies);
|
||||
~ConnectionView() override;
|
||||
@@ -70,14 +73,24 @@ public:
|
||||
BindingModel *bindingModel() const;
|
||||
BackendModel *backendModel() const;
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
|
||||
static ConnectionView *instance();
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
private: //variables
|
||||
QPointer<ConnectionViewWidget> m_connectionViewWidget;
|
||||
|
||||
ConnectionModel *m_connectionModel;
|
||||
BindingModel *m_bindingModel;
|
||||
DynamicPropertiesModel *m_dynamicPropertiesModel;
|
||||
BackendModel *m_backendModel;
|
||||
int m_currentIndex = 0;
|
||||
|
||||
QPointer<ConnectionViewQuickWidget> m_connectionViewQuickWidget;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -90,6 +90,8 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
|
||||
this, &ConnectionViewWidget::handleTabChanged);
|
||||
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
|
||||
ui->stackedWidget->parentWidget()->hide();
|
||||
}
|
||||
|
||||
ConnectionViewWidget::~ConnectionViewWidget()
|
||||
|
@@ -152,10 +152,34 @@ QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type)
|
||||
return expression;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::add()
|
||||
{
|
||||
addDynamicPropertyForCurrentNode();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::remove(int row)
|
||||
{
|
||||
deleteDynamicPropertyByRow(row);
|
||||
}
|
||||
|
||||
int DynamicPropertiesModel::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_view(parent)
|
||||
, m_explicitSelection(explicitSelection)
|
||||
: QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection),
|
||||
m_delegate(new DynamicPropertiesModelBackendDelegate(this))
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
|
||||
}
|
||||
@@ -163,6 +187,7 @@ DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractV
|
||||
void DynamicPropertiesModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
const int backIndex = m_currentIndex;
|
||||
clear();
|
||||
setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")});
|
||||
|
||||
@@ -172,7 +197,9 @@ void DynamicPropertiesModel::resetModel()
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
endResetModel();
|
||||
m_currentIndex = backIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +371,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
@@ -360,6 +389,8 @@ void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProper
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
@@ -368,6 +399,7 @@ void DynamicPropertiesModel::reset()
|
||||
m_handleDataChanged = false;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
|
||||
@@ -597,6 +629,8 @@ void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
updateCustomData(rowNumber, bindingProperty);
|
||||
|
||||
QString propertyName = QString::fromUtf8(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QString value = bindingProperty.expression();
|
||||
@@ -617,6 +651,7 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
updateCustomData(rowNumber, variantProperty);
|
||||
QString propertyName = QString::fromUtf8(variantProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QVariant value = variantProperty.value();
|
||||
@@ -787,6 +822,16 @@ void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const Abstrac
|
||||
{
|
||||
item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(property.name(), Qt::UserRole + 2);
|
||||
|
||||
item->setData(property.parentModelNode().id(), TargetNameRole);
|
||||
item->setData(property.name(), PropertyNameRole);
|
||||
item->setData(property.parentModelNode().id(), TargetNameRole);
|
||||
item->setData(property.dynamicTypeName(), PropertyTypeRole);
|
||||
|
||||
if (property.isVariantProperty())
|
||||
item->setData(property.toVariantProperty().value(), PropertyValueRole);
|
||||
if (property.isBindingProperty())
|
||||
item->setData(property.toBindingProperty().expression(), PropertyValueRole);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
|
||||
@@ -924,4 +969,217 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const
|
||||
return m_view->singleSelectedModelNode();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> DynamicPropertiesModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"},
|
||||
{PropertyNameRole, "name"},
|
||||
{PropertyTypeRole, "type"},
|
||||
{PropertyValueRole, "value"}};
|
||||
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const
|
||||
{
|
||||
return m_delegate;
|
||||
}
|
||||
|
||||
DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(
|
||||
DynamicPropertiesModel *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"});
|
||||
|
||||
connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this]() { handleTypeChanged(); });
|
||||
connect(&m_name, &StudioQmlTextBackend::activated, this, [this]() { handleNameChanged(); });
|
||||
connect(&m_value, &StudioQmlTextBackend::activated, this, [this]() { handleValueChanged(); });
|
||||
}
|
||||
|
||||
int DynamicPropertiesModelBackendDelegate::currentRow() const
|
||||
{
|
||||
return m_currentRow;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i)
|
||||
{
|
||||
if (m_currentRow == i)
|
||||
return;
|
||||
|
||||
m_currentRow = i;
|
||||
|
||||
//setup
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
|
||||
AbstractProperty property = model->abstractPropertyForRow(i);
|
||||
|
||||
m_type.setCurrentText(QString::fromUtf8(property.dynamicTypeName()));
|
||||
m_name.setText(QString::fromUtf8(property.name()));
|
||||
|
||||
if (property.isVariantProperty())
|
||||
m_value.setText(property.toVariantProperty().value().toString());
|
||||
else if (property.isBindingProperty())
|
||||
m_value.setText(property.toBindingProperty().expression());
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleTypeChanged()
|
||||
{
|
||||
//void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
const TypeName type = m_type.currentText().toUtf8();
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
|
||||
try {
|
||||
if (bindingProperty.isBindingProperty() || type == "var") { //var is always a binding
|
||||
const QString expression = bindingProperty.expression();
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
bindingProperty.setDynamicTypeNameAndExpression(type, expression);
|
||||
} else if (variantProperty.isVariantProperty()) {
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
variantProperty.setDynamicTypeNameAndValue(type, variantValue());
|
||||
}
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleNameChanged()
|
||||
{
|
||||
//see DynamicPropertiesModel::updatePropertyName
|
||||
|
||||
const PropertyName newName = m_name.text().toUtf8();
|
||||
QTC_ASSERT(!newName.isEmpty(), return );
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
model->view()->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() {
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
|
||||
targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType,
|
||||
expression);
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = variantProperty.value();
|
||||
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
|
||||
model->view()->executeInTransaction(__FUNCTION__, [=]() {
|
||||
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType,
|
||||
value);
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
});
|
||||
}
|
||||
|
||||
AbstractProperty property = targetNode.property(newName);
|
||||
|
||||
//order might have changed because of name change we have to select the correct row
|
||||
int newRow = model->findRowForProperty(property);
|
||||
model->setCurrentIndex(newRow);
|
||||
setCurrentRow(newRow);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleValueChanged()
|
||||
{
|
||||
//see void DynamicPropertiesModel::updateValue(int row)
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = m_value.text();
|
||||
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
try {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(),
|
||||
expression);
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
try {
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(),
|
||||
variantValue());
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
//reset
|
||||
}
|
||||
|
||||
QVariant DynamicPropertiesModelBackendDelegate::variantValue() const
|
||||
{
|
||||
//improve
|
||||
const QString type = m_type.currentText();
|
||||
if (type == "real" || type == "int")
|
||||
return m_value.text().toFloat();
|
||||
|
||||
if (type == "bool")
|
||||
return m_value.text() == "true";
|
||||
|
||||
return m_value.text();
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *DynamicPropertiesModelBackendDelegate::type()
|
||||
{
|
||||
return &m_type;
|
||||
}
|
||||
|
||||
StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::name()
|
||||
{
|
||||
return &m_name;
|
||||
}
|
||||
|
||||
StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::value()
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <nodeinstanceglobal.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
@@ -15,6 +17,8 @@ class BindingProperty;
|
||||
class ModelNode;
|
||||
class VariantProperty;
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate;
|
||||
|
||||
class DynamicPropertiesModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -27,6 +31,17 @@ public:
|
||||
PropertyValueRow = 3
|
||||
};
|
||||
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 2,
|
||||
TargetNameRole,
|
||||
PropertyNameRole,
|
||||
PropertyTypeRole,
|
||||
PropertyValueRole
|
||||
};
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
|
||||
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
@@ -62,6 +77,17 @@ public:
|
||||
static QVariant defaultValueForType(const TypeName &type);
|
||||
static QString defaultExpressionForType(const TypeName &type);
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
|
||||
int findRowForProperty(const AbstractProperty &abstractProperty) const;
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
protected:
|
||||
void addProperty(const QVariant &propertyValue,
|
||||
const QString &propertyType,
|
||||
@@ -79,12 +105,17 @@ protected:
|
||||
void updateCustomData(int row, const AbstractProperty &property);
|
||||
int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
|
||||
int findRowForVariantProperty(const VariantProperty &variantProperty) const;
|
||||
int findRowForProperty(const AbstractProperty &abstractProperty) const;
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty,
|
||||
QString *sourceNode,
|
||||
QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *delegate() const;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void handleException();
|
||||
@@ -95,6 +126,48 @@ private:
|
||||
QString m_exceptionError;
|
||||
QList<ModelNode> m_selectedNodes;
|
||||
bool m_explicitSelection = false;
|
||||
int m_currentIndex = 0;
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *m_delegate = nullptr;
|
||||
};
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT)
|
||||
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
|
||||
Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT)
|
||||
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
|
||||
//Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
|
||||
|
||||
public:
|
||||
DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
void nameChanged();
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleTypeChanged();
|
||||
void handleNameChanged();
|
||||
void handleValueChanged();
|
||||
void handleException();
|
||||
QVariant variantValue() const;
|
||||
|
||||
StudioQmlComboBoxBackend *type();
|
||||
|
||||
StudioQmlTextBackend *name();
|
||||
StudioQmlTextBackend *value();
|
||||
|
||||
StudioQmlComboBoxBackend m_type;
|
||||
StudioQmlTextBackend m_name;
|
||||
StudioQmlTextBackend m_value;
|
||||
int m_currentRow = -1;
|
||||
QString m_exceptionError;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -9,6 +9,127 @@
|
||||
#include <QQmlPropertyMap>
|
||||
#include <QtQuickWidgets/QQuickWidget>
|
||||
|
||||
class QMLDESIGNERBASE_EXPORT StudioQmlTextBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
|
||||
public:
|
||||
explicit StudioQmlTextBackend(QObject *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
void setText(const QString &text)
|
||||
{
|
||||
if (m_text == text)
|
||||
return;
|
||||
|
||||
m_text = text;
|
||||
emit textChanged();
|
||||
}
|
||||
|
||||
QString text() const { return m_text; }
|
||||
|
||||
Q_INVOKABLE void activateText(const QString &text)
|
||||
{
|
||||
if (m_text == text)
|
||||
return;
|
||||
|
||||
setText(text);
|
||||
emit activated(text);
|
||||
}
|
||||
|
||||
signals:
|
||||
void textChanged();
|
||||
void activated(const QString &text);
|
||||
|
||||
private:
|
||||
QString m_text;
|
||||
};
|
||||
|
||||
class QMLDESIGNERBASE_EXPORT StudioQmlComboBoxBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(QString currentText READ currentText WRITE setCurrentText NOTIFY currentTextChanged)
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
Q_PROPERTY(QStringList model READ model NOTIFY modelChanged) //TODO turn into model
|
||||
|
||||
public:
|
||||
explicit StudioQmlComboBoxBackend(QObject *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
void setModel(const QStringList &model)
|
||||
{
|
||||
if (m_model == model)
|
||||
return;
|
||||
m_model = model;
|
||||
emit countChanged();
|
||||
emit modelChanged();
|
||||
emit currentTextChanged();
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
QStringList model() const { return m_model; }
|
||||
|
||||
int count() const { return m_model.count(); }
|
||||
|
||||
QString currentText() const
|
||||
{
|
||||
if (m_currentIndex < 0)
|
||||
return {};
|
||||
|
||||
if (m_model.isEmpty())
|
||||
return {};
|
||||
|
||||
if (m_currentIndex >= m_model.count())
|
||||
return {};
|
||||
|
||||
return m_model.at(m_currentIndex);
|
||||
}
|
||||
|
||||
int currentIndex() const { return m_currentIndex; }
|
||||
|
||||
void setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
emit currentTextChanged();
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
void setCurrentText(const QString &text)
|
||||
{
|
||||
if (currentText() == text)
|
||||
return;
|
||||
|
||||
if (!m_model.contains(text))
|
||||
return;
|
||||
|
||||
setCurrentIndex(m_model.indexOf(text));
|
||||
}
|
||||
|
||||
Q_INVOKABLE void activateIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
setCurrentIndex(i);
|
||||
emit activated(i);
|
||||
}
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
void currentTextChanged();
|
||||
void countChanged();
|
||||
void modelChanged();
|
||||
void activated(int i);
|
||||
|
||||
private:
|
||||
int m_currentIndex = -1;
|
||||
QStringList m_model;
|
||||
};
|
||||
|
||||
class QMLDESIGNERBASE_EXPORT StudioPropertyMap : public QQmlPropertyMap
|
||||
{
|
||||
public:
|
||||
|
Reference in New Issue
Block a user