forked from qt-creator/qt-creator
QmlDesigner: Integrate Expression Builder
Task-number: QDS-10587 Change-Id: Ifc13a8364fccb74cb60d683f0e6c322d80baab50 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Henning Gründl
parent
cbf4273bab
commit
5e8b5ec1f0
@@ -107,19 +107,64 @@ Column {
|
|||||||
onClicked: backend.removeCondition()
|
onClicked: backend.removeCondition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExpressionBuilder {
|
||||||
|
style: StudioTheme.Values.connectionPopupControlStyle
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
visible: backend.hasCondition
|
||||||
|
model: backend.conditionListModel
|
||||||
|
|
||||||
|
onRemove: function(index) {
|
||||||
|
console.log("remove", index)
|
||||||
|
backend.conditionListModel.removeToken(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate: function(index, value) {
|
||||||
|
console.log("update", index, value)
|
||||||
|
backend.conditionListModel.updateToken(index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd: function(value) {
|
||||||
|
console.log("add", value)
|
||||||
|
backend.conditionListModel.appendToken(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onInsert: function(index, value, type) {
|
||||||
|
console.log("insert", index, value, type)
|
||||||
|
|
||||||
|
if (type === ConditionListModel.Intermediate)
|
||||||
|
backend.conditionListModel.insertIntermediateToken(index, value)
|
||||||
|
else if (type === ConditionListModel.Shadow)
|
||||||
|
backend.conditionListModel.insertShadowToken(index, value)
|
||||||
|
else
|
||||||
|
backend.conditionListModel.insertToken(index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetValue: function(index, value) {
|
||||||
|
console.log("setValue", index, value)
|
||||||
|
|
||||||
|
backend.conditionListModel.setShadowToken(index, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
spacing: root.horizontalSpacing
|
spacing: root.horizontalSpacing
|
||||||
width: root.width
|
width: root.width
|
||||||
Repeater {
|
|
||||||
|
|
||||||
|
Repeater {
|
||||||
model: backend.conditionListModel
|
model: backend.conditionListModel
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: value
|
text: value
|
||||||
color: "white"
|
color: "white"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
z: -1
|
z: -1
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
|
anchors.fill: parent
|
||||||
color: {
|
color: {
|
||||||
|
if (type === ConditionListModel.Intermediate)
|
||||||
|
return "darkorange"
|
||||||
if (type === ConditionListModel.Invalid)
|
if (type === ConditionListModel.Invalid)
|
||||||
return "red"
|
return "red"
|
||||||
if (type === ConditionListModel.Operator)
|
if (type === ConditionListModel.Operator)
|
||||||
@@ -128,8 +173,9 @@ Column {
|
|||||||
return "green"
|
return "green"
|
||||||
if (type === ConditionListModel.Variable)
|
if (type === ConditionListModel.Variable)
|
||||||
return "yellow"
|
return "yellow"
|
||||||
|
if (type === ConditionListModel.Shadow)
|
||||||
|
return "hotpink"
|
||||||
}
|
}
|
||||||
anchors.fill: parent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +199,8 @@ Column {
|
|||||||
iconSize: StudioTheme.Values.baseFontSize
|
iconSize: StudioTheme.Values.baseFontSize
|
||||||
iconFont: StudioTheme.Constants.font
|
iconFont: StudioTheme.Constants.font
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && !backend.hasElse
|
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom
|
||||||
|
&& backend.hasCondition && !backend.hasElse
|
||||||
|
|
||||||
onClicked: backend.addElse()
|
onClicked: backend.addElse()
|
||||||
}
|
}
|
||||||
@@ -165,7 +212,8 @@ Column {
|
|||||||
iconSize: StudioTheme.Values.baseFontSize
|
iconSize: StudioTheme.Values.baseFontSize
|
||||||
iconFont: StudioTheme.Constants.font
|
iconFont: StudioTheme.Constants.font
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse
|
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom
|
||||||
|
&& backend.hasCondition && backend.hasElse
|
||||||
|
|
||||||
onClicked: backend.removeElse()
|
onClicked: backend.removeElse()
|
||||||
}
|
}
|
||||||
@@ -177,7 +225,8 @@ Column {
|
|||||||
columnWidth: root.columnWidth
|
columnWidth: root.columnWidth
|
||||||
statement: backend.koStatement
|
statement: backend.koStatement
|
||||||
spacing: root.verticalSpacing
|
spacing: root.verticalSpacing
|
||||||
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse
|
visible: action.currentValue !== ConnectionModelStatementDelegate.Custom
|
||||||
|
&& backend.hasCondition && backend.hasElse
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
|
|||||||
@@ -0,0 +1,387 @@
|
|||||||
|
// 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 StudioControls as StudioControls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
|
property var conditionListModel: ConnectionsEditorEditorBackend.connectionModel.delegate.conditionListModel
|
||||||
|
|
||||||
|
property alias model: repeater.model
|
||||||
|
property int shadowPillIndex: -1
|
||||||
|
property bool shadowPillVisible: root.shadowPillIndex !== -1
|
||||||
|
|
||||||
|
property int heightBeforeShadowPill: Math.min(20, flow.childrenRect.height) // TODO Proper size value
|
||||||
|
property int expressionHeight: {
|
||||||
|
if (popup.visible)
|
||||||
|
return root.heightBeforeShadowPill + flow.spacing + 20
|
||||||
|
|
||||||
|
return root.heightBeforeShadowPill
|
||||||
|
}
|
||||||
|
|
||||||
|
signal remove(int index)
|
||||||
|
signal update(int index, var value)
|
||||||
|
signal add(var value)
|
||||||
|
signal insert(int index, var value, int type)
|
||||||
|
|
||||||
|
signal setValue(int index, var value)
|
||||||
|
signal setValueType(int index, var value, int type)
|
||||||
|
|
||||||
|
width: 400
|
||||||
|
height: root.expressionHeight + 2 * StudioTheme.Values.flowMargin
|
||||||
|
color: root.style.background.idle
|
||||||
|
border {
|
||||||
|
color: root.conditionListModel.valid ? root.style.border.idle
|
||||||
|
: StudioTheme.Values.themeError
|
||||||
|
width: root.style.borderWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!root.visible)
|
||||||
|
popup.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is text input for creating new items currently used.
|
||||||
|
function textInputActive() { // TODO Make property
|
||||||
|
return newTextInput.activeFocus && newTextInput.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMappedItemRect(index: int) : rect {
|
||||||
|
let item = repeater.itemAt(index)
|
||||||
|
let itemRect = Qt.rect(item.x, item.y, item.width, item.height)
|
||||||
|
return flow.mapToItem(root, itemRect)
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeCursor(index: int) : void {
|
||||||
|
var textInputPosition = Qt.point(0, 0)
|
||||||
|
|
||||||
|
if (!repeater.count) { // Empty repeater
|
||||||
|
let mappedItemRect = flow.mapToItem(root, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
textInputPosition = Qt.point(mappedItemRect.x, mappedItemRect.y)
|
||||||
|
index = 0
|
||||||
|
} else { // Repeater is not empty
|
||||||
|
// Clamp index to 0 and num items in repeater
|
||||||
|
index = Math.min(Math.max(index, 0), repeater.count)
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
// Needs to be placed in front of first repeater item
|
||||||
|
let mappedItemRect = root.getMappedItemRect(index)
|
||||||
|
textInputPosition = Qt.point(mappedItemRect.x - 4, // - 4 due to spacing of flow
|
||||||
|
mappedItemRect.y)
|
||||||
|
} else {
|
||||||
|
let mappedItemRect = root.getMappedItemRect(index - 1)
|
||||||
|
textInputPosition = Qt.point(mappedItemRect.x + mappedItemRect.width + 3,
|
||||||
|
mappedItemRect.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position text input, make it visible and set focus
|
||||||
|
newTextInput.x = textInputPosition.x
|
||||||
|
newTextInput.y = textInputPosition.y
|
||||||
|
newTextInput.index = index
|
||||||
|
newTextInput.visible = true
|
||||||
|
newTextInput.forceActiveFocus()
|
||||||
|
// Open suggestion popup
|
||||||
|
popup.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.ToolTip {
|
||||||
|
id: toolTip
|
||||||
|
visible: mouseArea.containsMouse && toolTip.text !== ""
|
||||||
|
delay: 1000
|
||||||
|
text: root.conditionListModel.error
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onPressed: function (event) {
|
||||||
|
// Check if empty
|
||||||
|
if (!repeater.count) {
|
||||||
|
root.placeCursor(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map to flow item
|
||||||
|
let point = mouseArea.mapToItem(flow, Qt.point(event.x, event.y))
|
||||||
|
|
||||||
|
let horizontalDistance = Number.MAX_VALUE
|
||||||
|
let verticalDistance = Number.MAX_VALUE
|
||||||
|
let cursorPosition = 0
|
||||||
|
|
||||||
|
for (var i = 0; i < repeater.count; ++i) {
|
||||||
|
let item = repeater.itemAt(i)
|
||||||
|
|
||||||
|
let y = item.y + (item.height / 2)
|
||||||
|
|
||||||
|
// Vertical distance
|
||||||
|
let vDistance = Math.abs(point.y - y)
|
||||||
|
|
||||||
|
// Horizontal distance
|
||||||
|
let hLeftDistance = Math.abs(point.x - item.x)
|
||||||
|
let hRightDistance = Math.abs(point.x - (item.x + item.width))
|
||||||
|
|
||||||
|
// Early return if vertical distance increases
|
||||||
|
if (vDistance > verticalDistance)
|
||||||
|
break
|
||||||
|
|
||||||
|
if (vDistance <= verticalDistance) {
|
||||||
|
// Rest horizontal distance if vertical distance is smaller than before
|
||||||
|
if (vDistance !== verticalDistance)
|
||||||
|
horizontalDistance = Number.MAX_VALUE
|
||||||
|
|
||||||
|
if (hLeftDistance < horizontalDistance) {
|
||||||
|
horizontalDistance = hLeftDistance
|
||||||
|
cursorPosition = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hRightDistance < horizontalDistance) {
|
||||||
|
horizontalDistance = hRightDistance
|
||||||
|
cursorPosition = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verticalDistance = vDistance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.placeCursor(cursorPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: flow
|
||||||
|
|
||||||
|
property int focusIndex: -1
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: StudioTheme.Values.flowMargin
|
||||||
|
spacing: StudioTheme.Values.flowSpacing
|
||||||
|
|
||||||
|
onPositioningComplete: {
|
||||||
|
if (root.textInputActive())
|
||||||
|
root.placeCursor(newTextInput.index)
|
||||||
|
|
||||||
|
if (!root.shadowPillVisible)
|
||||||
|
root.heightBeforeShadowPill = flow.childrenRect.height
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
onItemRemoved: function(index, item) {
|
||||||
|
if (!root.textInputActive())
|
||||||
|
return
|
||||||
|
|
||||||
|
// Udpate the cursor position
|
||||||
|
if (index < newTextInput.index)
|
||||||
|
newTextInput.index = newTextInput.index - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemAdded: function(index, item) {
|
||||||
|
if (!root.textInputActive())
|
||||||
|
return
|
||||||
|
|
||||||
|
if (index >= newTextInput.index)
|
||||||
|
newTextInput.index = newTextInput.index + 1
|
||||||
|
|
||||||
|
if (!root.conditionListModel.valid && index === root.conditionListModel.errorIndex)
|
||||||
|
item.invalid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: pill
|
||||||
|
|
||||||
|
onRemove: function() {
|
||||||
|
// If pill has focus due to selection or keyboard navigation
|
||||||
|
if (pill.focus)
|
||||||
|
root.placeCursor(pill.index)
|
||||||
|
|
||||||
|
Qt.callLater(root.remove, pill.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate: function(value) {
|
||||||
|
if (value === "")
|
||||||
|
Qt.callLater(root.remove, pill.index) // Otherwise crash
|
||||||
|
else
|
||||||
|
Qt.callLater(root.update, pill.index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocusChanged: function() {
|
||||||
|
if (pill.focus)
|
||||||
|
flow.focusIndex = pill.index
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit: {
|
||||||
|
console.log("SUBMIT")
|
||||||
|
|
||||||
|
//newTextInput.index = pill.index + 1
|
||||||
|
newTextInput.visible = true
|
||||||
|
newTextInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: newTextInput
|
||||||
|
|
||||||
|
property int index
|
||||||
|
|
||||||
|
height: 20
|
||||||
|
topPadding: 1
|
||||||
|
font.pixelSize: root.style.baseFontSize
|
||||||
|
color: root.style.text.idle
|
||||||
|
visible: false
|
||||||
|
validator: RegularExpressionValidator { regularExpression: /^\S.+/ }
|
||||||
|
|
||||||
|
//onActiveFocusChanged: {
|
||||||
|
// if (!newTextInput.activeFocus && !root.shadowPillVisible) {
|
||||||
|
// console.log("CLOSE POPUP")
|
||||||
|
// popup.close()
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
onTextEdited: {
|
||||||
|
if (newTextInput.text === "")
|
||||||
|
return
|
||||||
|
|
||||||
|
newTextInput.visible = false
|
||||||
|
|
||||||
|
root.insert(newTextInput.index, newTextInput.text, ConditionListModel.Intermediate)
|
||||||
|
|
||||||
|
newTextInput.clear()
|
||||||
|
|
||||||
|
// Set focus on the newly created item
|
||||||
|
let newItem = repeater.itemAt(newTextInput.index)
|
||||||
|
newItem.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (event.key === Qt.Key_Backspace) {
|
||||||
|
if (root.textInputActive()) {
|
||||||
|
let previousIndex = newTextInput.index - 1
|
||||||
|
if (previousIndex < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
let item = repeater.itemAt(previousIndex)
|
||||||
|
item.setCursorEnd()
|
||||||
|
item.forceActiveFocus()
|
||||||
|
popup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SuggestionPopup {
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
style: StudioTheme.Values.connectionPopupControlStyle
|
||||||
|
|
||||||
|
x: 0
|
||||||
|
y: root.height
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
//onOpened: console.log("POPUP opened")
|
||||||
|
//onClosed: console.log("POPUP closed")
|
||||||
|
|
||||||
|
onSelect: function(value) {
|
||||||
|
newTextInput.visible = true
|
||||||
|
newTextInput.forceActiveFocus()
|
||||||
|
|
||||||
|
if (root.shadowPillVisible) { // Active shadow pill
|
||||||
|
root.remove(root.shadowPillIndex)
|
||||||
|
root.shadowPillIndex = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
root.insert(newTextInput.index, value, ConditionListModel.Variable)
|
||||||
|
|
||||||
|
// Clear search, reset stack view and tree model
|
||||||
|
popup.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchActiveChanged: {
|
||||||
|
if (popup.searchActive) {
|
||||||
|
root.heightBeforeShadowPill = flow.childrenRect.height
|
||||||
|
root.insert(newTextInput.index, "...", ConditionListModel.Shadow)
|
||||||
|
root.shadowPillIndex = newTextInput.index
|
||||||
|
} else {
|
||||||
|
if (!root.shadowPillVisible)
|
||||||
|
return
|
||||||
|
|
||||||
|
root.remove(root.shadowPillIndex)
|
||||||
|
root.shadowPillIndex = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onEntered: function(value) {
|
||||||
|
if (!popup.searchActive) {
|
||||||
|
if (!root.shadowPillVisible) {
|
||||||
|
root.heightBeforeShadowPill = flow.childrenRect.height
|
||||||
|
root.shadowPillIndex = newTextInput.index
|
||||||
|
root.insert(newTextInput.index, value, ConditionListModel.Shadow)
|
||||||
|
} else {
|
||||||
|
root.setValue(root.shadowPillIndex, value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.setValue(root.shadowPillIndex, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: function(value) {
|
||||||
|
let shadowItem = repeater.itemAt(root.shadowPillIndex)
|
||||||
|
|
||||||
|
if (!popup.searchActive) {
|
||||||
|
if (root.shadowPillVisible && shadowItem?.value === value) {
|
||||||
|
root.remove(root.shadowPillIndex)
|
||||||
|
root.shadowPillIndex = -1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reset to 3 dots if still the same value as the exited item
|
||||||
|
if (shadowItem?.value === value)
|
||||||
|
root.setValue(root.shadowPillIndex, "...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (event.key === Qt.Key_Left) {
|
||||||
|
if (root.textInputActive()) {
|
||||||
|
let previousIndex = newTextInput.index - 1
|
||||||
|
if (previousIndex < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
let item = repeater.itemAt(previousIndex)
|
||||||
|
item.setCursorEnd()
|
||||||
|
item.forceActiveFocus()
|
||||||
|
popup.close()
|
||||||
|
} else {
|
||||||
|
if (flow.focusIndex < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
root.placeCursor(flow.focusIndex)
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_Right) {
|
||||||
|
if (root.textInputActive()) {
|
||||||
|
let nextIndex = newTextInput.index
|
||||||
|
if (nextIndex >= repeater.count)
|
||||||
|
return
|
||||||
|
|
||||||
|
let item = repeater.itemAt(nextIndex)
|
||||||
|
item.setCursorBegin()
|
||||||
|
item.forceActiveFocus()
|
||||||
|
popup.close()
|
||||||
|
} else {
|
||||||
|
root.placeCursor(flow.focusIndex + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
id: control
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: 8
|
||||||
|
text: control.text
|
||||||
|
font: control.font
|
||||||
|
opacity: enabled ? 1.0 : 0.3
|
||||||
|
color: control.hovered ? "#111111" : "white"
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: 200
|
||||||
|
implicitHeight: 30
|
||||||
|
opacity: enabled ? 1 : 0.3
|
||||||
|
color: control.hovered ? "#4DBFFF" : "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// 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.Templates as T
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
T.TreeViewDelegate {
|
||||||
|
id: control
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
implicitWidth: 200
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
//implicitWidth: leftMargin + __contentIndent + implicitContentWidth + rightPadding + rightMargin
|
||||||
|
//implicitHeight: Math.max(indicator ? indicator.height : 0, implicitContentHeight) * 1.25
|
||||||
|
|
||||||
|
indentation: 12
|
||||||
|
//leftMargin: 4
|
||||||
|
//rightMargin: 4
|
||||||
|
//spacing: 4
|
||||||
|
|
||||||
|
//topPadding: contentItem ? (height - contentItem.implicitHeight) / 2 : 0
|
||||||
|
leftPadding: control.leftMargin + control.__contentIndent
|
||||||
|
|
||||||
|
//required property int row
|
||||||
|
//required property var model
|
||||||
|
readonly property real __contentIndent: !control.isTreeNode ? 0
|
||||||
|
: (control.depth * control.indentation)
|
||||||
|
+ (control.indicator ? control.indicator.width + control.spacing : 0)
|
||||||
|
|
||||||
|
indicator: Item {
|
||||||
|
readonly property real __indicatorIndent: control.leftMargin + (control.depth * control.indentation)
|
||||||
|
|
||||||
|
x: __indicatorIndent
|
||||||
|
width: 30
|
||||||
|
height: 30
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: caret
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: StudioTheme.Values.smallIconFontSize
|
||||||
|
color: control.hovered ? "#111111" : "white" // TODO colors
|
||||||
|
text: StudioTheme.Constants.sectionToggle
|
||||||
|
rotation: control.expanded ? 0 : -90
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: 200
|
||||||
|
implicitHeight: 30
|
||||||
|
color: control.hovered ? "#4DBFFF" : "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
text: control.text
|
||||||
|
font: control.font
|
||||||
|
opacity: enabled ? 1.0 : 0.3
|
||||||
|
color: control.hovered ? "#111111" : "white"
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
167
share/qtcreator/qmldesigner/connectionseditor/Pill.qml
Normal file
167
share/qtcreator/qmldesigner/connectionseditor/Pill.qml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// 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 StudioControls as StudioControls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property string value
|
||||||
|
required property int type
|
||||||
|
|
||||||
|
function setCursorBegin() { textInput.cursorPosition = 0 }
|
||||||
|
function setCursorEnd() { textInput.cursorPosition = textInput.text.length }
|
||||||
|
|
||||||
|
function isEditable() { return root.type === ConditionListModel.Intermediate
|
||||||
|
|| root.type === ConditionListModel.Literal
|
||||||
|
|| root.type === ConditionListModel.Invalid }
|
||||||
|
|
||||||
|
function isIntermediate() { return root.type === ConditionListModel.Intermediate }
|
||||||
|
function isLiteral() { return root.type === ConditionListModel.Literal }
|
||||||
|
function isOperator() { return root.type === ConditionListModel.Operator }
|
||||||
|
function isProperty() { return root.type === ConditionListModel.Variable }
|
||||||
|
function isShadow() { return root.type === ConditionListModel.Shadow }
|
||||||
|
function isInvalid() { return root.type === ConditionListModel.Invalid || root.invalid }
|
||||||
|
|
||||||
|
signal remove()
|
||||||
|
signal update(var value)
|
||||||
|
signal submit()
|
||||||
|
|
||||||
|
readonly property int margin: StudioTheme.Values.flowPillMargin
|
||||||
|
|
||||||
|
property bool invalid: false
|
||||||
|
|
||||||
|
width: {
|
||||||
|
if (root.isEditable()) {
|
||||||
|
if (root.isInvalid())
|
||||||
|
return textInput.width + 1 + 2 * root.margin
|
||||||
|
else
|
||||||
|
return textInput.width + 1
|
||||||
|
}
|
||||||
|
return textItem.contentWidth + icon.width + root.margin
|
||||||
|
}
|
||||||
|
height: StudioTheme.Values.flowPillHeight
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (root.activeFocus && root.isEditable())
|
||||||
|
textInput.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (root.isEditable())
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.key === Qt.Key_Backspace || event.key === Qt.Key_Delete)
|
||||||
|
root.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: rootMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: root.isEditable() ? Qt.IBeamCursor : Qt.ArrowCursor
|
||||||
|
onClicked: root.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pill
|
||||||
|
anchors.fill: parent
|
||||||
|
color: {
|
||||||
|
if (root.isShadow())
|
||||||
|
return StudioTheme.Values.themeInteraction
|
||||||
|
if (root.isEditable())
|
||||||
|
return "transparent"
|
||||||
|
|
||||||
|
return StudioTheme.Values.themePillBackground
|
||||||
|
}
|
||||||
|
border.color: root.isInvalid() ? StudioTheme.Values.themeWarning : "white" // TODO colors
|
||||||
|
border.width: {
|
||||||
|
if (root.isShadow())
|
||||||
|
return 0
|
||||||
|
if (root.isInvalid())
|
||||||
|
return 1
|
||||||
|
if (root.isEditable())
|
||||||
|
return 0
|
||||||
|
if (rootMouseArea.containsMouse || root.focus)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: root.margin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.isOperator() || root.isProperty() || root.isShadow()
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: textItem
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
color: root.isShadow() ? StudioTheme.Values.themeTextSelectedTextColor
|
||||||
|
: StudioTheme.Values.themeTextColor
|
||||||
|
text: root.value
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: icon
|
||||||
|
width: root.isShadow() ? root.margin : StudioTheme.Values.flowPillHeight
|
||||||
|
height: StudioTheme.Values.flowPillHeight
|
||||||
|
visible: !root.isShadow()
|
||||||
|
|
||||||
|
Text {
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: StudioTheme.Values.smallIconFontSize
|
||||||
|
color: StudioTheme.Values.themeIconColor
|
||||||
|
text: StudioTheme.Constants.close_small
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: textInput
|
||||||
|
|
||||||
|
property bool dirty: false
|
||||||
|
|
||||||
|
x: root.isInvalid() ? root.margin : 0
|
||||||
|
height: StudioTheme.Values.flowPillHeight
|
||||||
|
topPadding: 1
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
color: (rootMouseArea.containsMouse || textInput.activeFocus) ? StudioTheme.Values.themeIconColor
|
||||||
|
: StudioTheme.Values.themeTextColor
|
||||||
|
text: root.value
|
||||||
|
visible: root.isEditable()
|
||||||
|
enabled: root.isEditable()
|
||||||
|
|
||||||
|
validator: RegularExpressionValidator { regularExpression: /^\S+/ }
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.update(textInput.text) // emit
|
||||||
|
root.submit() // emit
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited: textInput.dirty = true
|
||||||
|
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
if (event.key === Qt.Key_Backspace) {
|
||||||
|
if (textInput.text !== "")
|
||||||
|
return
|
||||||
|
|
||||||
|
root.remove() // emit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
// 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 as Controls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
import StudioControls as StudioControls
|
||||||
|
import ConnectionsEditorEditorBackend
|
||||||
|
|
||||||
|
Controls.Popup {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
|
||||||
|
|
||||||
|
property var listModel: ConnectionsEditorEditorBackend.connectionModel.delegate.propertyListProxyModel
|
||||||
|
property var treeModel: ConnectionsEditorEditorBackend.connectionModel.delegate.propertyTreeModel
|
||||||
|
|
||||||
|
signal select(var value)
|
||||||
|
signal entered(var value)
|
||||||
|
signal exited(var value)
|
||||||
|
|
||||||
|
property alias searchActive: search.activeFocus
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
search.clear()
|
||||||
|
stack.pop(null, Controls.StackView.Immediate)
|
||||||
|
root.listModel.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
closePolicy: Controls.Popup.NoAutoClose
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: root.width
|
||||||
|
color: root.style.background.idle
|
||||||
|
border {
|
||||||
|
color: root.style.border.idle
|
||||||
|
width: root.style.borderWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Column {
|
||||||
|
StudioControls.SearchBox {
|
||||||
|
id: search
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
onSearchChanged: function(value) {
|
||||||
|
root.treeModel.setFilter(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controls.StackView {
|
||||||
|
id: stack
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: currentItem?.implicitHeight
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
initialItem: mainView
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: mainView
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Rectangle {
|
||||||
|
width: stack.width
|
||||||
|
height: 30
|
||||||
|
visible: root.listModel.parentName !== ""
|
||||||
|
color: backMouseArea.containsMouse ? "#4DBFFF" : "transparent"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: backMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
stack.pop(Controls.StackView.Immediate)
|
||||||
|
root.listModel.goUp() //treeModel.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 30
|
||||||
|
height: 30
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: chevronLeft
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: root.style.baseIconFontSize
|
||||||
|
color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors
|
||||||
|
text: StudioTheme.Constants.back_medium
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: root.listModel.parentName
|
||||||
|
color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: stack.width - 8
|
||||||
|
height: 1
|
||||||
|
visible: root.listModel.parentName !== ""
|
||||||
|
color: "#3C3C3C"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
visible: search.empty
|
||||||
|
width: stack.width
|
||||||
|
implicitHeight: Math.min(380, childrenRect.height)
|
||||||
|
clip: true
|
||||||
|
model: root.listModel
|
||||||
|
|
||||||
|
delegate: MyListViewDelegate {
|
||||||
|
id: listViewDelegate
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
required property string propertyName
|
||||||
|
required property int childCount
|
||||||
|
required property string expression
|
||||||
|
|
||||||
|
text: listViewDelegate.propertyName
|
||||||
|
implicitWidth: listView.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (!listViewDelegate.childCount) {
|
||||||
|
root.select(listViewDelegate.expression)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push(mainView, Controls.StackView.Immediate)
|
||||||
|
|
||||||
|
ListView.view.model.goInto(listViewDelegate.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (listViewDelegate.childCount)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (listViewDelegate.hovered)
|
||||||
|
root.entered(listViewDelegate.expression)
|
||||||
|
else
|
||||||
|
root.exited(listViewDelegate.expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeView {
|
||||||
|
id: treeView
|
||||||
|
visible: !search.empty
|
||||||
|
width: stack.width
|
||||||
|
implicitHeight: Math.min(380, childrenRect.height)
|
||||||
|
clip: true
|
||||||
|
model: root.treeModel
|
||||||
|
|
||||||
|
// This is currently a workaround and should be cleaned up. Calling
|
||||||
|
// expandRecursively every time the filter changes is performance wise not good.
|
||||||
|
//Connections {
|
||||||
|
// target: proxyModel
|
||||||
|
// function onFilterChanged() { treeView.expandRecursively() }
|
||||||
|
//}
|
||||||
|
|
||||||
|
delegate: MyTreeViewDelegate {
|
||||||
|
id: treeViewDelegate
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
required property string propertyName
|
||||||
|
required property int childCount
|
||||||
|
required property string expression
|
||||||
|
|
||||||
|
text: treeViewDelegate.propertyName
|
||||||
|
implicitWidth: treeView.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (!treeViewDelegate.childCount)
|
||||||
|
root.select(treeViewDelegate.expression)
|
||||||
|
else
|
||||||
|
treeView.toggleExpanded(treeViewDelegate.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (treeViewDelegate.childCount)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (treeViewDelegate.hovered)
|
||||||
|
root.entered(treeViewDelegate.expression)
|
||||||
|
else
|
||||||
|
root.exited(treeViewDelegate.expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: false
|
||||||
|
width: stack.width
|
||||||
|
height: flow.childrenRect.height + 2 * StudioTheme.Values.flowMargin
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: flow
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: StudioTheme.Values.flowMargin
|
||||||
|
spacing: StudioTheme.Values.flowSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
// TODO actual value + tooltip
|
||||||
|
model: ["AND", "OR", "equal", "not equal", "greater", "less", "greater then", "less then"]
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: textItem.contentWidth + 14
|
||||||
|
height: 26
|
||||||
|
color: "#161616"
|
||||||
|
radius: 4
|
||||||
|
border {
|
||||||
|
color: "white"
|
||||||
|
width: mouseArea.containsMouse ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: textItem
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: "white"
|
||||||
|
text: modelData
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -131,6 +131,7 @@ T.TabButton {
|
|||||||
State {
|
State {
|
||||||
name: "check"
|
name: "check"
|
||||||
when: control.enabled && !control.pressed && control.checked
|
when: control.enabled && !control.pressed && control.checked
|
||||||
|
extend: "hoverCheck"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: controlBackground
|
target: controlBackground
|
||||||
color: control.style.interaction
|
color: control.style.interaction
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (C) 2021 The Qt Company Ltd.
|
// Copyright (C) 2021 The Qt Company Ltd.
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts
|
||||||
import HelperWidgets 2.0
|
import HelperWidgets
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ T.TextField {
|
|||||||
|
|
||||||
signal searchChanged(string searchText)
|
signal searchChanged(string searchText)
|
||||||
|
|
||||||
|
property bool empty: control.text === ""
|
||||||
|
|
||||||
function isEmpty() {
|
function isEmpty() {
|
||||||
return control.text === ""
|
return control.text === ""
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@ T.TextField {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: control.searchChanged(text)
|
onTextChanged: control.searchChanged(control.text)
|
||||||
|
|
||||||
T.Label {
|
T.Label {
|
||||||
id: searchIcon
|
id: searchIcon
|
||||||
|
|||||||
@@ -238,6 +238,12 @@ QtObject {
|
|||||||
property real dialogButtonSpacing: 10
|
property real dialogButtonSpacing: 10
|
||||||
property real dialogButtonPadding: 4
|
property real dialogButtonPadding: 4
|
||||||
|
|
||||||
|
// NEW NEW NEW
|
||||||
|
readonly property int flowMargin: 7
|
||||||
|
readonly property int flowSpacing: 7 // Odd so cursor has a center location
|
||||||
|
readonly property int flowPillMargin: 4
|
||||||
|
readonly property int flowPillHeight: 20
|
||||||
|
|
||||||
// Theme Colors
|
// Theme Colors
|
||||||
|
|
||||||
property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue
|
property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue
|
||||||
@@ -434,6 +440,10 @@ QtObject {
|
|||||||
property color themeDialogBackground: values.themeThumbnailBackground
|
property color themeDialogBackground: values.themeThumbnailBackground
|
||||||
property color themeDialogOutline: values.themeInteraction
|
property color themeDialogOutline: values.themeInteraction
|
||||||
|
|
||||||
|
// Expression Builder
|
||||||
|
property color themePillBackground: Theme.color(Theme.DSdockWidgetSplitter)
|
||||||
|
|
||||||
|
|
||||||
// Control Style Mapping
|
// Control Style Mapping
|
||||||
property ControlStyle controlStyle: DefaultStyle {}
|
property ControlStyle controlStyle: DefaultStyle {}
|
||||||
property ControlStyle connectionPopupControlStyle: ConnectionPopupControlStyle {}
|
property ControlStyle connectionPopupControlStyle: ConnectionPopupControlStyle {}
|
||||||
|
|||||||
@@ -559,8 +559,13 @@ QHash<int, QByteArray> ConnectionModel::roleNames() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent)
|
ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent)
|
||||||
: QObject(parent), m_signalDelegate(parent->connectionView()), m_okStatementDelegate(parent),
|
: QObject(parent)
|
||||||
m_koStatementDelegate(parent), m_conditionListModel(parent)
|
, m_signalDelegate(parent->connectionView())
|
||||||
|
, m_okStatementDelegate(parent)
|
||||||
|
, m_koStatementDelegate(parent)
|
||||||
|
, m_conditionListModel(parent)
|
||||||
|
, m_propertyTreeModel(parent->connectionView())
|
||||||
|
, m_propertyListProxyModel(&m_propertyTreeModel)
|
||||||
{
|
{
|
||||||
connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() {
|
connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() {
|
||||||
handleTargetChanged();
|
handleTargetChanged();
|
||||||
@@ -753,6 +758,9 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i)
|
|||||||
|
|
||||||
m_currentRow = i;
|
m_currentRow = i;
|
||||||
|
|
||||||
|
m_propertyTreeModel.resetModel();
|
||||||
|
m_propertyListProxyModel.setRowAndInternalId(0, -1);
|
||||||
|
|
||||||
//setup
|
//setup
|
||||||
|
|
||||||
ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
|
ConnectionModel *model = qobject_cast<ConnectionModel *>(parent());
|
||||||
@@ -870,6 +878,16 @@ void ConnectionModelBackendDelegate::setSource(const QString &source)
|
|||||||
emit sourceChanged();
|
emit sourceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyTreeModel *ConnectionModelBackendDelegate::propertyTreeModel()
|
||||||
|
{
|
||||||
|
return &m_propertyTreeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyListProxyModel *ConnectionModelBackendDelegate::propertyListProxyModel()
|
||||||
|
{
|
||||||
|
return &m_propertyListProxyModel;
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionModelBackendDelegate::setupCondition()
|
void ConnectionModelBackendDelegate::setupCondition()
|
||||||
{
|
{
|
||||||
auto &condition = ConnectionEditorStatements::matchedCondition(m_handler);
|
auto &condition = ConnectionEditorStatements::matchedCondition(m_handler);
|
||||||
@@ -1575,30 +1593,83 @@ ConditionListModel::ConditionToken ConditionListModel::tokenFromComparativeState
|
|||||||
|
|
||||||
void ConditionListModel::insertToken(int index, const QString &value)
|
void ConditionListModel::insertToken(int index, const QString &value)
|
||||||
{
|
{
|
||||||
|
beginInsertRows({}, index, index);
|
||||||
|
|
||||||
m_tokens.insert(index, valueToToken(value));
|
m_tokens.insert(index, valueToToken(value));
|
||||||
validateAndRebuildTokens();
|
validateAndRebuildTokens();
|
||||||
resetModel();
|
|
||||||
|
endInsertRows();
|
||||||
|
//resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::updateToken(int index, const QString &value)
|
void ConditionListModel::updateToken(int index, const QString &value)
|
||||||
{
|
{
|
||||||
m_tokens[index] = valueToToken(value);
|
m_tokens[index] = valueToToken(value);
|
||||||
validateAndRebuildTokens();
|
validateAndRebuildTokens();
|
||||||
resetModel();
|
|
||||||
|
dataChanged(createIndex(index, 0), createIndex(index, 0));
|
||||||
|
//resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::appendToken(const QString &value)
|
void ConditionListModel::appendToken(const QString &value)
|
||||||
{
|
{
|
||||||
|
beginInsertRows({}, rowCount() - 1, rowCount() - 1);
|
||||||
|
|
||||||
insertToken(rowCount(), value);
|
insertToken(rowCount(), value);
|
||||||
validateAndRebuildTokens();
|
validateAndRebuildTokens();
|
||||||
resetModel();
|
|
||||||
|
endInsertRows();
|
||||||
|
//resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::removeToken(int index)
|
void ConditionListModel::removeToken(int index)
|
||||||
{
|
{
|
||||||
|
beginRemoveRows({}, index, index);
|
||||||
|
|
||||||
m_tokens.remove(index, 1);
|
m_tokens.remove(index, 1);
|
||||||
validateAndRebuildTokens();
|
validateAndRebuildTokens();
|
||||||
resetModel();
|
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
//resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionListModel::insertIntermediateToken(int index, const QString &value)
|
||||||
|
{
|
||||||
|
beginInsertRows({}, index, index);
|
||||||
|
|
||||||
|
ConditionToken token;
|
||||||
|
token.type = Intermediate;
|
||||||
|
token.value = value;
|
||||||
|
|
||||||
|
m_tokens.insert(index, token);
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
//resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionListModel::insertShadowToken(int index, const QString &value)
|
||||||
|
{
|
||||||
|
beginInsertRows({}, index, index);
|
||||||
|
|
||||||
|
ConditionToken token;
|
||||||
|
token.type = Shadow;
|
||||||
|
token.value = value;
|
||||||
|
|
||||||
|
m_tokens.insert(index, token);
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
//resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionListModel::setShadowToken(int index, const QString &value)
|
||||||
|
{
|
||||||
|
m_tokens[index].type = Shadow;
|
||||||
|
m_tokens[index].value = value;
|
||||||
|
|
||||||
|
dataChanged(createIndex(index, 0), createIndex(index, 0));
|
||||||
|
//resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConditionListModel::valid() const
|
bool ConditionListModel::valid() const
|
||||||
@@ -1655,20 +1726,29 @@ void ConditionListModel::command(const QString &string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::setInvalid(const QString &errorMessage)
|
void ConditionListModel::setInvalid(const QString &errorMessage, int index)
|
||||||
{
|
{
|
||||||
m_valid = false;
|
m_valid = false;
|
||||||
m_errorMessage = errorMessage;
|
m_errorMessage = errorMessage;
|
||||||
|
|
||||||
emit errorChanged();
|
emit errorChanged();
|
||||||
emit validChanged();
|
emit validChanged();
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
m_errorIndex = index;
|
||||||
|
emit errorIndexChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::setValid()
|
void ConditionListModel::setValid()
|
||||||
{
|
{
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
m_errorMessage.clear();
|
m_errorMessage.clear();
|
||||||
|
m_errorIndex = -1;
|
||||||
|
|
||||||
emit errorChanged();
|
emit errorChanged();
|
||||||
emit validChanged();
|
emit validChanged();
|
||||||
|
emit errorIndexChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ConditionListModel::error() const
|
QString ConditionListModel::error() const
|
||||||
@@ -1676,6 +1756,11 @@ QString ConditionListModel::error() const
|
|||||||
return m_errorMessage;
|
return m_errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ConditionListModel::errorIndex() const
|
||||||
|
{
|
||||||
|
return m_errorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
void ConditionListModel::internalSetup()
|
void ConditionListModel::internalSetup()
|
||||||
{
|
{
|
||||||
setInvalid(tr("No Valid Condition"));
|
setInvalid(tr("No Valid Condition"));
|
||||||
@@ -1766,11 +1851,26 @@ int ConditionListModel::checkOrder() const
|
|||||||
it++;
|
it++;
|
||||||
ret++;
|
ret++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wasOperator)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConditionListModel::validateAndRebuildTokens()
|
void ConditionListModel::validateAndRebuildTokens()
|
||||||
{
|
{
|
||||||
|
/// NEW
|
||||||
|
auto it = m_tokens.begin();
|
||||||
|
|
||||||
|
while (it != m_tokens.end()) {
|
||||||
|
if (it->type == Intermediate)
|
||||||
|
*it = valueToToken(it->value);
|
||||||
|
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
// NEW
|
||||||
|
|
||||||
QString invalidValue;
|
QString invalidValue;
|
||||||
const bool invalidToken = Utils::contains(m_tokens,
|
const bool invalidToken = Utils::contains(m_tokens,
|
||||||
[&invalidValue](const ConditionToken &token) {
|
[&invalidValue](const ConditionToken &token) {
|
||||||
@@ -1780,12 +1880,12 @@ void ConditionListModel::validateAndRebuildTokens()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (invalidToken) {
|
if (invalidToken) {
|
||||||
setInvalid(tr("Invalid token %").arg(invalidToken));
|
setInvalid(tr("Invalid token %1").arg(invalidValue));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int firstError = checkOrder() != -1) {
|
if (int firstError = checkOrder() != -1) {
|
||||||
setInvalid(tr("Invalid order at %1").arg(firstError));
|
setInvalid(tr("Invalid order at %1").arg(firstError), firstError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1831,9 +1931,6 @@ ConnectionEditorStatements::ConditionToken ConditionListModel::toOperatorStateme
|
|||||||
if (token.value == "!==")
|
if (token.value == "!==")
|
||||||
return ConnectionEditorStatements::ConditionToken::Not;
|
return ConnectionEditorStatements::ConditionToken::Not;
|
||||||
|
|
||||||
if (token.value == "!==")
|
|
||||||
return ConnectionEditorStatements::ConditionToken::Not;
|
|
||||||
|
|
||||||
if (token.value == ">")
|
if (token.value == ">")
|
||||||
return ConnectionEditorStatements::ConditionToken::LargerThan;
|
return ConnectionEditorStatements::ConditionToken::LargerThan;
|
||||||
|
|
||||||
|
|||||||
@@ -96,9 +96,10 @@ class ConditionListModel : public QAbstractListModel
|
|||||||
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
|
Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
|
||||||
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
|
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
|
||||||
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
|
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
|
||||||
|
Q_PROPERTY(int errorIndex READ errorIndex NOTIFY errorIndexChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ConditionType { Invalid, Operator, Literal, Variable };
|
enum ConditionType { Intermediate, Invalid, Operator, Literal, Variable, Shadow };
|
||||||
Q_ENUM(ConditionType)
|
Q_ENUM(ConditionType)
|
||||||
|
|
||||||
struct ConditionToken
|
struct ConditionToken
|
||||||
@@ -129,22 +130,28 @@ public:
|
|||||||
Q_INVOKABLE void appendToken(const QString &value);
|
Q_INVOKABLE void appendToken(const QString &value);
|
||||||
Q_INVOKABLE void removeToken(int index);
|
Q_INVOKABLE void removeToken(int index);
|
||||||
|
|
||||||
|
Q_INVOKABLE void insertIntermediateToken(int index, const QString &value);
|
||||||
|
Q_INVOKABLE void insertShadowToken(int index, const QString &value);
|
||||||
|
Q_INVOKABLE void setShadowToken(int index, const QString &value);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
||||||
//for debugging
|
//for debugging
|
||||||
Q_INVOKABLE void command(const QString &string);
|
Q_INVOKABLE void command(const QString &string);
|
||||||
|
|
||||||
void setInvalid(const QString &errorMessage);
|
void setInvalid(const QString &errorMessage, int index = -1);
|
||||||
void setValid();
|
void setValid();
|
||||||
|
|
||||||
QString error() const;
|
QString error() const;
|
||||||
|
int errorIndex() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void validChanged();
|
void validChanged();
|
||||||
void emptyChanged();
|
void emptyChanged();
|
||||||
void conditionChanged();
|
void conditionChanged();
|
||||||
void errorChanged();
|
void errorChanged();
|
||||||
|
void errorIndexChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void internalSetup();
|
void internalSetup();
|
||||||
@@ -162,6 +169,7 @@ private:
|
|||||||
QList<ConditionToken> m_tokens;
|
QList<ConditionToken> m_tokens;
|
||||||
bool m_valid = false;
|
bool m_valid = false;
|
||||||
QString m_errorMessage;
|
QString m_errorMessage;
|
||||||
|
int m_errorIndex = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConnectionModelStatementDelegate : public QObject
|
class ConnectionModelStatementDelegate : public QObject
|
||||||
@@ -245,6 +253,9 @@ class ConnectionModelBackendDelegate : public QObject
|
|||||||
Q_PROPERTY(bool hasElse READ hasElse NOTIFY hasElseChanged)
|
Q_PROPERTY(bool hasElse READ hasElse NOTIFY hasElseChanged)
|
||||||
Q_PROPERTY(QString source READ source NOTIFY sourceChanged)
|
Q_PROPERTY(QString source READ source NOTIFY sourceChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(PropertyTreeModel *propertyTreeModel READ propertyTreeModel CONSTANT)
|
||||||
|
Q_PROPERTY(PropertyListProxyModel *propertyListProxyModel READ propertyListProxyModel CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConnectionModelBackendDelegate(ConnectionModel *parent = nullptr);
|
explicit ConnectionModelBackendDelegate(ConnectionModel *parent = nullptr);
|
||||||
|
|
||||||
@@ -282,6 +293,10 @@ private:
|
|||||||
ConditionListModel *conditionListModel();
|
ConditionListModel *conditionListModel();
|
||||||
QString source() const;
|
QString source() const;
|
||||||
void setSource(const QString &source);
|
void setSource(const QString &source);
|
||||||
|
|
||||||
|
PropertyTreeModel *propertyTreeModel();
|
||||||
|
PropertyListProxyModel *propertyListProxyModel();
|
||||||
|
|
||||||
void setupCondition();
|
void setupCondition();
|
||||||
void setupHandlerAndStatements();
|
void setupHandlerAndStatements();
|
||||||
|
|
||||||
@@ -303,6 +318,8 @@ private:
|
|||||||
bool m_hasCondition = false;
|
bool m_hasCondition = false;
|
||||||
bool m_hasElse = false;
|
bool m_hasElse = false;
|
||||||
QString m_source;
|
QString m_source;
|
||||||
|
PropertyTreeModel m_propertyTreeModel;
|
||||||
|
PropertyListProxyModel m_propertyListProxyModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|||||||
@@ -88,10 +88,13 @@ public:
|
|||||||
0,
|
0,
|
||||||
"ConnectionModelStatementDelegate");
|
"ConnectionModelStatementDelegate");
|
||||||
|
|
||||||
qmlRegisterType<ConditionListModel>("ConnectionsEditorEditorBackend",
|
qmlRegisterType<ConditionListModel>("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel");
|
||||||
|
|
||||||
|
qmlRegisterType<PropertyTreeModel>("ConnectionsEditorEditorBackend", 1, 0, "PropertyTreeModel");
|
||||||
|
qmlRegisterType<PropertyListProxyModel>("ConnectionsEditorEditorBackend",
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
"ConditionListModel");
|
"PropertyListProxyModel");
|
||||||
|
|
||||||
Theme::setupTheme(engine());
|
Theme::setupTheme(engine());
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,8 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == RowRole)
|
if (role == RowRole)
|
||||||
return index.row();
|
return index.row();
|
||||||
|
|
||||||
if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole) {
|
if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole
|
||||||
|
|| role == ChildCountRole) {
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@@ -166,18 +167,18 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
DataCacheItem item = m_indexHash[index.internalId()];
|
DataCacheItem item = m_indexHash[index.internalId()];
|
||||||
|
|
||||||
if (item.propertyName.isEmpty()) { //node
|
if (role == ChildCountRole)
|
||||||
if (role == PropertyNameRole)
|
return rowCount(index);
|
||||||
return item.modelNode.displayName();
|
|
||||||
|
|
||||||
return true; //nodes are always shown
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == ExpressionRole)
|
if (role == ExpressionRole)
|
||||||
return QString(item.modelNode.id() + item.propertyName);
|
return QString(item.modelNode.id() + "." + item.propertyName);
|
||||||
|
|
||||||
if (role == PropertyNameRole)
|
if (role == PropertyNameRole) {
|
||||||
return item.propertyName;
|
if (!item.propertyName.isEmpty())
|
||||||
|
return QString::fromUtf8(item.propertyName);
|
||||||
|
else
|
||||||
|
return item.modelNode.displayName();
|
||||||
|
}
|
||||||
|
|
||||||
static const auto priority = properityLists();
|
static const auto priority = properityLists();
|
||||||
if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end())
|
if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end())
|
||||||
@@ -187,6 +188,13 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end())
|
if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end())
|
||||||
return true; // dynamic properties have priority
|
return true; // dynamic properties have priority
|
||||||
|
|
||||||
|
if (item.propertyName.isEmpty()) { //node
|
||||||
|
//if (role == PropertyNameRole)
|
||||||
|
// return item.modelNode.displayName();
|
||||||
|
|
||||||
|
return true; // nodes are always shown
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +224,7 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
return item.propertyName;
|
return QString::fromUtf8(item.propertyName);
|
||||||
|
|
||||||
QFont f;
|
QFont f;
|
||||||
auto priority = properityLists();
|
auto priority = properityLists();
|
||||||
@@ -241,7 +249,7 @@ QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &par
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!hasIndex(row, column, parent))
|
if (!hasIndex(row, column, parent))
|
||||||
return QModelIndex();
|
return {};
|
||||||
|
|
||||||
const int rootId = -1;
|
const int rootId = -1;
|
||||||
|
|
||||||
@@ -312,7 +320,7 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const
|
|||||||
return ensureModelIndex(item.modelNode, row);
|
return ensureModelIndex(item.modelNode, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPersistentModelIndex PropertyTreeModel::indexForInernalIdAndRow(int internalId, int row)
|
QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(int internalId, int row)
|
||||||
{
|
{
|
||||||
return createIndex(row, 0, internalId);
|
return createIndex(row, 0, internalId);
|
||||||
}
|
}
|
||||||
@@ -323,7 +331,7 @@ int PropertyTreeModel::rowCount(const QModelIndex &parent) const
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!parent.isValid())
|
if (!parent.isValid())
|
||||||
return 1;
|
return 1; //m_nodeList.size();
|
||||||
|
|
||||||
int internalId = parent.internalId();
|
int internalId = parent.internalId();
|
||||||
|
|
||||||
@@ -783,7 +791,8 @@ QHash<int, QByteArray> PropertyTreeModel::roleNames() const
|
|||||||
{
|
{
|
||||||
static QHash<int, QByteArray> roleNames{{PropertyNameRole, "propertyName"},
|
static QHash<int, QByteArray> roleNames{{PropertyNameRole, "propertyName"},
|
||||||
{PropertyPriorityRole, "hasPriority"},
|
{PropertyPriorityRole, "hasPriority"},
|
||||||
{ExpressionRole, "expression"}};
|
{ExpressionRole, "expression"},
|
||||||
|
{ChildCountRole, "childCount"}};
|
||||||
|
|
||||||
return roleNames;
|
return roleNames;
|
||||||
}
|
}
|
||||||
@@ -792,17 +801,24 @@ PropertyListProxyModel::PropertyListProxyModel(PropertyTreeModel *parent)
|
|||||||
: QAbstractListModel(), m_treeModel(parent)
|
: QAbstractListModel(), m_treeModel(parent)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void PropertyListProxyModel::setRowandInternalId(int row, int internalId)
|
void PropertyListProxyModel::resetModel()
|
||||||
{
|
{
|
||||||
|
beginResetModel();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyListProxyModel::setRowAndInternalId(int row, int internalId)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << row << internalId;
|
||||||
QTC_ASSERT(m_treeModel, return );
|
QTC_ASSERT(m_treeModel, return );
|
||||||
|
|
||||||
if (internalId == -1)
|
if (internalId == -1)
|
||||||
m_parentIndex = m_treeModel->index(0, 0);
|
m_parentIndex = m_treeModel->index(0, 0);
|
||||||
else
|
else
|
||||||
m_parentIndex = m_treeModel->indexForInernalIdAndRow(internalId, row);
|
m_parentIndex = m_treeModel->index(row, 0, m_parentIndex);
|
||||||
|
//m_parentIndex = m_treeModel->indexForInternalIdAndRow(internalId, row);
|
||||||
|
|
||||||
beginResetModel();
|
resetModel();
|
||||||
endResetModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PropertyListProxyModel::rowCount(const QModelIndex &) const
|
int PropertyListProxyModel::rowCount(const QModelIndex &) const
|
||||||
@@ -820,6 +836,40 @@ QVariant PropertyListProxyModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_treeModel->data(treeIndex, role);
|
return m_treeModel->data(treeIndex, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> PropertyListProxyModel::roleNames() const
|
||||||
|
{
|
||||||
|
return m_treeModel->roleNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyListProxyModel::goInto(int row)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << row << m_parentIndex.internalId();
|
||||||
|
setRowAndInternalId(row, 0); //m_parentIndex.internalId());
|
||||||
|
|
||||||
|
emit parentNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyListProxyModel::goUp()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
m_parentIndex = m_treeModel->parent(m_parentIndex);
|
||||||
|
resetModel();
|
||||||
|
|
||||||
|
emit parentNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyListProxyModel::reset()
|
||||||
|
{
|
||||||
|
setRowAndInternalId(0, -1); // TODO ???
|
||||||
|
|
||||||
|
emit parentNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PropertyListProxyModel::parentName() const
|
||||||
|
{
|
||||||
|
return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString();
|
||||||
|
}
|
||||||
|
|
||||||
PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent)
|
PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent)
|
||||||
{
|
{
|
||||||
connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public:
|
|||||||
PropertyNameRole = Qt::UserRole + 1,
|
PropertyNameRole = Qt::UserRole + 1,
|
||||||
PropertyPriorityRole,
|
PropertyPriorityRole,
|
||||||
ExpressionRole,
|
ExpressionRole,
|
||||||
|
ChildCountRole,
|
||||||
RowRole,
|
RowRole,
|
||||||
InternalIdRole
|
InternalIdRole
|
||||||
};
|
};
|
||||||
@@ -57,7 +58,7 @@ public:
|
|||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QModelIndex parent(const QModelIndex &index) const override;
|
QModelIndex parent(const QModelIndex &index) const override;
|
||||||
|
|
||||||
QPersistentModelIndex indexForInernalIdAndRow(int internalId, int row);
|
QPersistentModelIndex indexForInternalIdAndRow(int internalId, int row);
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
@@ -70,14 +71,13 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void setPropertyType(PropertyTypes type);
|
void setPropertyType(PropertyTypes type);
|
||||||
void setFilter(const QString &filter);
|
Q_INVOKABLE void setFilter(const QString &filter);
|
||||||
|
|
||||||
QList<ModelNode> nodeList() const;
|
QList<ModelNode> nodeList() const;
|
||||||
|
|
||||||
const std::vector<PropertyName> getProperties(const ModelNode &modelNode) const;
|
const std::vector<PropertyName> getProperties(const ModelNode &modelNode) const;
|
||||||
ModelNode getModelNodeForId(const QString &id) const;
|
ModelNode getModelNodeForId(const QString &id) const;
|
||||||
|
|
||||||
protected:
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -127,12 +127,29 @@ private:
|
|||||||
class PropertyListProxyModel : public QAbstractListModel
|
class PropertyListProxyModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString parentName READ parentName NOTIFY parentNameChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PropertyListProxyModel(PropertyTreeModel *parent);
|
PropertyListProxyModel(PropertyTreeModel *parent);
|
||||||
void setRowandInternalId(int row, int internalId);
|
|
||||||
|
void resetModel();
|
||||||
|
|
||||||
|
void setRowAndInternalId(int row, int internalId);
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void goInto(int row);
|
||||||
|
Q_INVOKABLE void goUp();
|
||||||
|
Q_INVOKABLE void reset();
|
||||||
|
|
||||||
|
QString parentName() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void parentNameChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModelNode m_modelNode;
|
ModelNode m_modelNode;
|
||||||
PropertyName m_propertyName;
|
PropertyName m_propertyName;
|
||||||
|
|||||||
Reference in New Issue
Block a user