forked from qt-creator/qt-creator
QmlDesigner: Add new states editor
* Add responsive layout algorithm for states editor * Add support for extending states * Drag and drop states * Display when condition in state thumbnail * Toggle between property changes and thumbnail * Display PropertyChanges properties * Make property changes removable * Add StateGroup support * Add toolbar Task-number: QDS-7639 Change-Id: Iabcbb0a02f7728c29abbdc1000102582c5638eba Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Thomas Hartmann
parent
89535f1025
commit
9ffc004beb
832
share/qtcreator/qmldesigner/newstateseditor/Main.qml
Normal file
832
share/qtcreator/qmldesigner/newstateseditor/Main.qml
Normal file
@@ -0,0 +1,832 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import StatesEditor
|
||||||
|
import StudioControls 1.0 as StudioControls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal createNewState
|
||||||
|
signal cloneState(int internalNodeId)
|
||||||
|
signal extendState(int internalNodeId)
|
||||||
|
signal deleteState(int internalNodeId)
|
||||||
|
|
||||||
|
property bool isLandscape: true
|
||||||
|
|
||||||
|
color: StudioTheme.Values.themeStatePanelBackground
|
||||||
|
|
||||||
|
onWidthChanged: root.responsiveResize(root.width, root.height)
|
||||||
|
onHeightChanged: root.responsiveResize(root.width, root.height)
|
||||||
|
|
||||||
|
Component.onCompleted: root.responsiveResize(root.width, root.height)
|
||||||
|
|
||||||
|
function numFit(overall, size, space) {
|
||||||
|
let tmpNum = Math.floor(overall / size)
|
||||||
|
let spaceLeft = overall - (tmpNum * size)
|
||||||
|
return spaceLeft - (space * (tmpNum - 1)) >= 0 ? tmpNum : tmpNum - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function responsiveResize(width, height) {
|
||||||
|
height -= toolBar.height + (2 * root.padding)
|
||||||
|
width -= (2 * root.padding)
|
||||||
|
|
||||||
|
var numStates = statesRepeater.count - 1 // Subtract base state
|
||||||
|
var numRows = 0
|
||||||
|
var numColumns = 0
|
||||||
|
|
||||||
|
// Size extension in case of extend groups are shown
|
||||||
|
var sizeExtension = root.showExtendGroups ? root.extend : 0
|
||||||
|
var doubleSizeExtension = root.showExtendGroups ? 2 * root.extend : 0
|
||||||
|
|
||||||
|
// Get view orientation (LANDSCAPE, PORTRAIT)
|
||||||
|
if (width >= height) {
|
||||||
|
root.isLandscape = true
|
||||||
|
outerGrid.columns = 3
|
||||||
|
outerGrid.rows = 1
|
||||||
|
// Three outer section height (base state, middle, plus button)
|
||||||
|
baseStateWrapper.height = height
|
||||||
|
root.scrollViewHeight = height
|
||||||
|
addWrapper.height = height
|
||||||
|
|
||||||
|
height -= doubleSizeExtension
|
||||||
|
|
||||||
|
if (height > Constants.maxThumbSize) {
|
||||||
|
// In this case we want to have a multi row grid in the center
|
||||||
|
root.thumbSize = Constants.maxThumbSize
|
||||||
|
|
||||||
|
let tmpScrollViewWidth = width - root.thumbSize * 1.5 - 2 * root.outerGridSpacing
|
||||||
|
|
||||||
|
// Inner grid calculation
|
||||||
|
numRows = root.numFit(height, Constants.maxThumbSize, root.innerGridSpacing)
|
||||||
|
numColumns = Math.min(numStates, root.numFit(tmpScrollViewWidth, root.thumbSize,
|
||||||
|
root.innerGridSpacing))
|
||||||
|
|
||||||
|
let tmpRows = Math.ceil(numStates / numColumns)
|
||||||
|
|
||||||
|
if (tmpRows <= numRows)
|
||||||
|
numRows = tmpRows
|
||||||
|
else
|
||||||
|
numColumns = Math.ceil(numStates / numRows)
|
||||||
|
} else {
|
||||||
|
// This case is for single row layout and small thumb view
|
||||||
|
root.thumbSize = Math.max(height, Constants.minThumbSize)
|
||||||
|
|
||||||
|
// Inner grid calculation
|
||||||
|
numColumns = numStates
|
||||||
|
numRows = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Constants.thumbnailSize = root.thumbSize
|
||||||
|
|
||||||
|
let tmpWidth = root.thumbSize * numColumns + root.innerGridSpacing * (numColumns - 1) + doubleSizeExtension
|
||||||
|
let remainingSpace = width - root.thumbSize - 2 * root.outerGridSpacing
|
||||||
|
let space = remainingSpace - tmpWidth
|
||||||
|
|
||||||
|
if (space >= root.thumbSize) {
|
||||||
|
root.scrollViewWidth = tmpWidth
|
||||||
|
addWrapper.width = space
|
||||||
|
} else {
|
||||||
|
addWrapper.width = Math.max(space, 0.5 * root.thumbSize)
|
||||||
|
root.scrollViewWidth = remainingSpace - addWrapper.width
|
||||||
|
}
|
||||||
|
|
||||||
|
root.topMargin = (root.scrollViewHeight - (root.thumbSize * numRows)
|
||||||
|
- root.innerGridSpacing * (numRows - 1)) * 0.5 - sizeExtension
|
||||||
|
|
||||||
|
addCanvas.width = Math.min(addWrapper.width, root.thumbSize)
|
||||||
|
addCanvas.height = root.thumbSize
|
||||||
|
|
||||||
|
baseStateWrapper.width = root.thumbSize
|
||||||
|
|
||||||
|
baseStateThumbnail.anchors.verticalCenter = baseStateWrapper.verticalCenter
|
||||||
|
baseStateThumbnail.anchors.horizontalCenter = undefined
|
||||||
|
|
||||||
|
addCanvas.anchors.verticalCenter = addWrapper.verticalCenter
|
||||||
|
addCanvas.anchors.horizontalCenter = undefined
|
||||||
|
addCanvas.anchors.top = undefined
|
||||||
|
addCanvas.anchors.left = addWrapper.left
|
||||||
|
|
||||||
|
root.leftMargin = 0 // resetting left margin in case of orientation switch
|
||||||
|
} else {
|
||||||
|
root.isLandscape = false
|
||||||
|
outerGrid.rows = 3
|
||||||
|
outerGrid.columns = 1
|
||||||
|
// Three outer section width (base state, middle, plus button)
|
||||||
|
baseStateWrapper.width = width
|
||||||
|
root.scrollViewWidth = width
|
||||||
|
addWrapper.width = width
|
||||||
|
|
||||||
|
width -= doubleSizeExtension
|
||||||
|
|
||||||
|
if (width > Constants.maxThumbSize) {
|
||||||
|
// In this case we want to have a multi column grid in the center
|
||||||
|
root.thumbSize = Constants.maxThumbSize
|
||||||
|
|
||||||
|
let tmpScrollViewHeight = height - root.thumbSize * 1.5 - 2 * root.outerGridSpacing
|
||||||
|
|
||||||
|
// Inner grid calculation
|
||||||
|
numRows = Math.min(numStates, root.numFit(tmpScrollViewHeight, root.thumbSize,
|
||||||
|
root.innerGridSpacing))
|
||||||
|
numColumns = root.numFit(width, Constants.maxThumbSize, root.innerGridSpacing)
|
||||||
|
|
||||||
|
let tmpColumns = Math.ceil(numStates / numRows)
|
||||||
|
|
||||||
|
if (tmpColumns <= numColumns)
|
||||||
|
numColumns = tmpColumns
|
||||||
|
else
|
||||||
|
numRows = Math.ceil(numStates / numColumns)
|
||||||
|
} else {
|
||||||
|
// This case is for single column layout and small thumb view
|
||||||
|
root.thumbSize = Math.max(width, Constants.minThumbSize)
|
||||||
|
|
||||||
|
// Inner grid calculation
|
||||||
|
numRows = numStates
|
||||||
|
numColumns = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Constants.thumbnailSize = root.thumbSize
|
||||||
|
|
||||||
|
let tmpHeight = root.thumbSize * numRows + root.innerGridSpacing * (numRows - 1) + doubleSizeExtension
|
||||||
|
let remainingSpace = height - root.thumbSize - 2 * root.outerGridSpacing
|
||||||
|
let space = remainingSpace - tmpHeight
|
||||||
|
|
||||||
|
if (space >= root.thumbSize) {
|
||||||
|
root.scrollViewHeight = tmpHeight
|
||||||
|
addWrapper.height = space
|
||||||
|
} else {
|
||||||
|
addWrapper.height = Math.max(space, 0.5 * root.thumbSize)
|
||||||
|
root.scrollViewHeight = remainingSpace - addWrapper.height
|
||||||
|
}
|
||||||
|
|
||||||
|
root.leftMargin = (root.scrollViewWidth - (root.thumbSize * numColumns)
|
||||||
|
- root.innerGridSpacing * (numColumns - 1)) * 0.5 - sizeExtension
|
||||||
|
|
||||||
|
addCanvas.width = root.thumbSize
|
||||||
|
addCanvas.height = Math.min(addWrapper.height, root.thumbSize)
|
||||||
|
|
||||||
|
baseStateWrapper.height = root.thumbSize
|
||||||
|
|
||||||
|
baseStateThumbnail.anchors.verticalCenter = undefined
|
||||||
|
baseStateThumbnail.anchors.horizontalCenter = baseStateWrapper.horizontalCenter
|
||||||
|
|
||||||
|
addCanvas.anchors.verticalCenter = undefined
|
||||||
|
addCanvas.anchors.horizontalCenter = addWrapper.horizontalCenter
|
||||||
|
addCanvas.anchors.top = addWrapper.top
|
||||||
|
addCanvas.anchors.left = undefined
|
||||||
|
|
||||||
|
root.topMargin = 0 // resetting top margin in case of orientation switch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always assign the bigger one first otherwise there will be console output complaining
|
||||||
|
if (numRows > innerGrid.rows) {
|
||||||
|
innerGrid.rows = numRows
|
||||||
|
innerGrid.columns = numColumns
|
||||||
|
} else {
|
||||||
|
innerGrid.columns = numColumns
|
||||||
|
innerGrid.rows = numRows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These function assume that the order of the states is as follows:
|
||||||
|
// State A, State B (extends State A), ... so the extended state always comes first
|
||||||
|
function isInRange(i) {
|
||||||
|
return i >= 0 && i < statesEditorModel.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextStateHasExtend(i) {
|
||||||
|
let next = i + 1
|
||||||
|
return root.isInRange(next) ? statesEditorModel.get(next).hasExtend : false
|
||||||
|
}
|
||||||
|
|
||||||
|
function previousStateHasExtend(i) {
|
||||||
|
let prev = i - 1
|
||||||
|
return root.isInRange(prev) ? statesEditorModel.get(prev).hasExtend : false
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool showExtendGroups: statesEditorModel.hasExtend
|
||||||
|
|
||||||
|
onShowExtendGroupsChanged: root.responsiveResize(root.width, root.height)
|
||||||
|
|
||||||
|
property int extend: 16
|
||||||
|
|
||||||
|
property int thumbSize: 250
|
||||||
|
|
||||||
|
property int padding: 10
|
||||||
|
|
||||||
|
property int scrollViewWidth: 640
|
||||||
|
property int scrollViewHeight: 480
|
||||||
|
property int outerGridSpacing: 10
|
||||||
|
property int innerGridSpacing: root.showExtendGroups ? 40 : root.outerGridSpacing
|
||||||
|
|
||||||
|
// These margins are used to push the inner grid down or to the left depending on the views
|
||||||
|
// orientation to align to the outer grid
|
||||||
|
property int topMargin: 0
|
||||||
|
property int leftMargin: 0
|
||||||
|
|
||||||
|
property bool tinyMode: Constants.thumbnailSize <= Constants.thumbnailBreak
|
||||||
|
|
||||||
|
property int currentStateInternalId: 0
|
||||||
|
|
||||||
|
// This timer is used to delay the current state animation as it didn't work due to the
|
||||||
|
// repeaters item not being positioned in time resulting in 0 x and y position if the grids
|
||||||
|
// row and column were not changed during the layout algorithm .
|
||||||
|
Timer {
|
||||||
|
id: layoutTimer
|
||||||
|
interval: 50
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
// Move the current state into view if outside
|
||||||
|
if (root.currentStateInternalId === 0)
|
||||||
|
// Not for base state
|
||||||
|
return
|
||||||
|
|
||||||
|
var x = 0
|
||||||
|
var y = 0
|
||||||
|
for (var i = 0; i < statesRepeater.count; ++i) {
|
||||||
|
let item = statesRepeater.itemAt(i)
|
||||||
|
|
||||||
|
if (item.internalNodeId === root.currentStateInternalId) {
|
||||||
|
x = item.x
|
||||||
|
y = item.y
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it is in view
|
||||||
|
if (x <= frame.contentX
|
||||||
|
|| x >= (frame.contentX + root.scrollViewWidth - root.thumbSize))
|
||||||
|
frame.contentX = x - root.scrollViewWidth * 0.5 + root.thumbSize * 0.5
|
||||||
|
|
||||||
|
if (y <= frame.contentY
|
||||||
|
|| y >= (frame.contentY + root.scrollViewHeight - root.thumbSize))
|
||||||
|
frame.contentY = y - root.scrollViewHeight * 0.5 + root.thumbSize * 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentStateInternalIdChanged: layoutTimer.start()
|
||||||
|
|
||||||
|
StudioControls.Dialog {
|
||||||
|
id: editDialog
|
||||||
|
title: qsTr("Rename state group")
|
||||||
|
standardButtons: Dialog.Apply | Dialog.Cancel
|
||||||
|
x: editButton.x - Math.max(0, editButton.x + editDialog.width - root.width)
|
||||||
|
y: toolBar.height
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
|
||||||
|
width: Math.min(300, root.width)
|
||||||
|
|
||||||
|
onApplied: {
|
||||||
|
let renamed = statesEditorModel.renameActiveStateGroup(editTextField.text)
|
||||||
|
if (renamed)
|
||||||
|
editDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.TextField {
|
||||||
|
id: editTextField
|
||||||
|
text: statesEditorModel.activeStateGroup
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
translationIndicatorVisible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: toolBar
|
||||||
|
|
||||||
|
property bool doubleRow: root.width < 450
|
||||||
|
|
||||||
|
onDoubleRowChanged: {
|
||||||
|
if (toolBar.doubleRow) {
|
||||||
|
toolBarGrid.rows = 2
|
||||||
|
toolBarGrid.columns = 1
|
||||||
|
} else {
|
||||||
|
toolBarGrid.columns = 2
|
||||||
|
toolBarGrid.rows = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
width: root.width
|
||||||
|
height: (toolBar.doubleRow ? 2 : 1) * StudioTheme.Values.toolbarHeight
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: toolBarGrid
|
||||||
|
columns: 2
|
||||||
|
rows: 1
|
||||||
|
columnSpacing: StudioTheme.Values.toolbarSpacing
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: stateGroupSelectionRow
|
||||||
|
height: StudioTheme.Values.toolbarHeight
|
||||||
|
spacing: StudioTheme.Values.toolbarSpacing
|
||||||
|
leftPadding: root.padding
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: stateGroupLabel
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("State Group")
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
height: StudioTheme.Values.height
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: root.width > 240
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.ComboBox {
|
||||||
|
id: stateGroupComboBox
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
model: statesEditorModel.stateGroups
|
||||||
|
currentIndex: statesEditorModel.activeStateGroupIndex
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: stateGroupLabel.visible ? StudioTheme.Values.defaultControlWidth
|
||||||
|
: root.width - 2 * root.padding
|
||||||
|
|
||||||
|
popup.onOpened: editDialog.close()
|
||||||
|
|
||||||
|
// currentIndex needs special treatment, because if model is changed, it will be
|
||||||
|
// reset regardless of binding.
|
||||||
|
Connections {
|
||||||
|
target: statesEditorModel
|
||||||
|
function onActiveStateGroupIndexChanged() {
|
||||||
|
stateGroupComboBox.currentIndex = statesEditorModel.activeStateGroupIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelChanged: {
|
||||||
|
stateGroupComboBox.currentIndex = statesEditorModel.activeStateGroupIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
onCompressedActivated: function (index, reason) {
|
||||||
|
statesEditorModel.activeStateGroupIndex = index
|
||||||
|
root.responsiveResize(root.width, root.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
Row {
|
||||||
|
id: stateGroupEditRow
|
||||||
|
height: StudioTheme.Values.toolbarHeight
|
||||||
|
spacing: StudioTheme.Values.toolbarSpacing
|
||||||
|
leftPadding: toolBar.doubleRow ? root.padding : 0
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
buttonIcon: StudioTheme.Constants.plus
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: statesEditorModel.addStateGroup("stateGroup")
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
buttonIcon: StudioTheme.Constants.minus
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
enabled: statesEditorModel.activeStateGroupIndex !== 0
|
||||||
|
onClicked: statesEditorModel.removeStateGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
id: editButton
|
||||||
|
buttonIcon: StudioTheme.Constants.edit
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
enabled: statesEditorModel.activeStateGroupIndex !== 0
|
||||||
|
checked: editDialog.visible
|
||||||
|
onClicked: {
|
||||||
|
if (editDialog.opened)
|
||||||
|
editDialog.close()
|
||||||
|
else
|
||||||
|
editDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Math.max(0, toolBar.width - (toolBar.doubleRow ? 0 : (stateGroupSelectionRow.width
|
||||||
|
+ toolBarGrid.columnSpacing))
|
||||||
|
- stateGroupEditRow.width - thumbnailToggleRow.width)
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: thumbnailToggleRow
|
||||||
|
height: StudioTheme.Values.toolbarHeight
|
||||||
|
spacing: StudioTheme.Values.toolbarSpacing
|
||||||
|
rightPadding: root.padding
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
buttonIcon: StudioTheme.Constants.gridView
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
enabled: !root.tinyMode
|
||||||
|
onClicked: {
|
||||||
|
for (var i = 0; i < statesRepeater.count; ++i)
|
||||||
|
statesRepeater.itemAt(i).setPropertyChangesVisible(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
buttonIcon: StudioTheme.Constants.textFullJustification
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
enabled: !root.tinyMode
|
||||||
|
onClicked: {
|
||||||
|
for (var i = 0; i < statesRepeater.count; ++i)
|
||||||
|
statesRepeater.itemAt(i).setPropertyChangesVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: outerGrid
|
||||||
|
x: root.padding
|
||||||
|
y: toolBar.height + root.padding
|
||||||
|
columns: 3
|
||||||
|
rows: 1
|
||||||
|
spacing: root.outerGridSpacing
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: baseStateWrapper
|
||||||
|
|
||||||
|
StateThumbnail {
|
||||||
|
// Base State
|
||||||
|
id: baseStateThumbnail
|
||||||
|
width: Constants.thumbnailSize
|
||||||
|
height: Constants.thumbnailSize
|
||||||
|
baseState: true
|
||||||
|
defaultChecked: !statesEditorModel.baseState.modelHasDefaultState // TODO Make this one a model property
|
||||||
|
isChecked: root.currentStateInternalId === 0
|
||||||
|
thumbnailImageSource: statesEditorModel.baseState.stateImageSource // TODO Get rid of the QVariantMap
|
||||||
|
isTiny: root.tinyMode
|
||||||
|
|
||||||
|
onFocusSignal: root.currentStateInternalId = 0
|
||||||
|
onDefaultClicked: statesEditorModel.resetDefaultState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: scrollViewWrapper
|
||||||
|
width: root.isLandscape ? root.scrollViewWidth : root.width - (2 * root.padding)
|
||||||
|
height: root.isLandscape ? root.height - toolBar.height - (2 * root.padding) : root.scrollViewHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: root.topMargin
|
||||||
|
anchors.leftMargin: root.leftMargin
|
||||||
|
|
||||||
|
ScrollBar.horizontal: StateScrollBar {
|
||||||
|
parent: scrollView
|
||||||
|
x: scrollView.leftPadding
|
||||||
|
y: scrollView.height - height
|
||||||
|
width: scrollView.availableWidth
|
||||||
|
active: scrollView.ScrollBar.vertical.active
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: StateScrollBar {
|
||||||
|
parent: scrollView
|
||||||
|
x: scrollView.mirrored ? 0 : scrollView.width - width
|
||||||
|
y: scrollView.topPadding
|
||||||
|
height: scrollView.availableHeight
|
||||||
|
active: scrollView.ScrollBar.horizontal.active
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: frame
|
||||||
|
boundsMovement: Flickable.StopAtBounds
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
interactive: true
|
||||||
|
contentWidth: {
|
||||||
|
let ext = root.showExtendGroups ? (2 * root.extend) : 0
|
||||||
|
return innerGrid.width + ext
|
||||||
|
}
|
||||||
|
contentHeight: {
|
||||||
|
let ext = root.showExtendGroups ? (2 * root.extend) : 0
|
||||||
|
return innerGrid.height + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on contentY {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on contentX {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: innerGrid
|
||||||
|
|
||||||
|
x: root.showExtendGroups ? root.extend : 0
|
||||||
|
y: root.showExtendGroups ? root.extend : 0
|
||||||
|
|
||||||
|
rows: 1
|
||||||
|
spacing: root.innerGridSpacing
|
||||||
|
|
||||||
|
move: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "x,y"
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: statesRepeater
|
||||||
|
|
||||||
|
property int grabIndex: -1
|
||||||
|
|
||||||
|
function executeDrop(from, to) {
|
||||||
|
statesEditorModel.drop(from, to)
|
||||||
|
statesRepeater.grabIndex = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
model: statesEditorModel
|
||||||
|
|
||||||
|
onItemAdded: root.responsiveResize(root.width, root.height)
|
||||||
|
onItemRemoved: root.responsiveResize(root.width, root.height)
|
||||||
|
|
||||||
|
delegate: DropArea {
|
||||||
|
id: delegateRoot
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
required property string stateName
|
||||||
|
required property var stateImageSource
|
||||||
|
required property int internalNodeId
|
||||||
|
required property var hasWhenCondition
|
||||||
|
required property var whenConditionString
|
||||||
|
required property bool isDefault
|
||||||
|
required property var modelHasDefaultState
|
||||||
|
required property bool hasExtend
|
||||||
|
required property var extendString
|
||||||
|
|
||||||
|
function setPropertyChangesVisible(value) {
|
||||||
|
stateThumbnail.propertyChangesVisible = value
|
||||||
|
}
|
||||||
|
|
||||||
|
width: Constants.thumbnailSize
|
||||||
|
height: Constants.thumbnailSize
|
||||||
|
|
||||||
|
visible: delegateRoot.internalNodeId // Skip base state
|
||||||
|
|
||||||
|
property int visualIndex: index
|
||||||
|
|
||||||
|
onEntered: function (drag) {
|
||||||
|
let dragSource = (drag.source as StateThumbnail)
|
||||||
|
|
||||||
|
if (dragSource === undefined)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (dragSource.extendString !== stateThumbnail.extendString
|
||||||
|
|| stateThumbnail.extendedState) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
statesEditorModel.move(
|
||||||
|
(drag.source as StateThumbnail).visualIndex,
|
||||||
|
stateThumbnail.visualIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropped: function (drop) {
|
||||||
|
let dragSource = (drop.source as StateThumbnail)
|
||||||
|
|
||||||
|
if (dragSource === undefined)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (dragSource.extendString !== stateThumbnail.extendString
|
||||||
|
|| stateThumbnail.extendedState) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
statesRepeater.executeDrop(statesRepeater.grabIndex,
|
||||||
|
stateThumbnail.visualIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend Groups Visualization
|
||||||
|
Rectangle {
|
||||||
|
id: extendBackground
|
||||||
|
x: -root.extend
|
||||||
|
y: -root.extend
|
||||||
|
width: Constants.thumbnailSize + 2 * root.extend
|
||||||
|
height: Constants.thumbnailSize + 2 * root.extend
|
||||||
|
color: StudioTheme.Values.themeStateHighlight
|
||||||
|
|
||||||
|
radius: {
|
||||||
|
if (root.nextStateHasExtend(delegateRoot.index))
|
||||||
|
return delegateRoot.hasExtend ? 0 : root.extend
|
||||||
|
|
||||||
|
return root.extend
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: (delegateRoot.hasExtend
|
||||||
|
|| stateThumbnail.extendedState)
|
||||||
|
}
|
||||||
|
// Fill the gap between extend group states and also cover up radius
|
||||||
|
// of start and end states of an extend group in case of line break
|
||||||
|
Rectangle {
|
||||||
|
id: extendGap
|
||||||
|
property bool portraitOneColumn: !root.isLandscape
|
||||||
|
&& innerGrid.columns === 1
|
||||||
|
|
||||||
|
property bool leftOrTop: {
|
||||||
|
if (delegateRoot.hasExtend)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (root.previousStateHasExtend(delegateRoot.index))
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
property bool rightOrBottom: {
|
||||||
|
if (stateThumbnail.extendedState)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (root.nextStateHasExtend(delegateRoot.index))
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool firstInRow: ((delegateRoot.index - 1) % innerGrid.columns) === 0
|
||||||
|
property bool lastInRow: ((delegateRoot.index - 1) % innerGrid.columns)
|
||||||
|
=== (innerGrid.columns - 1)
|
||||||
|
|
||||||
|
x: {
|
||||||
|
if (!extendGap.portraitOneColumn) {
|
||||||
|
if (extendGap.rightOrBottom)
|
||||||
|
return extendGap.lastInRow ? Constants.thumbnailSize
|
||||||
|
- (root.innerGridSpacing
|
||||||
|
- root.extend) : Constants.thumbnailSize
|
||||||
|
if (extendGap.leftOrTop)
|
||||||
|
return extendGap.firstInRow ? -root.extend : -root.innerGridSpacing
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return -root.extend
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
if (extendGap.portraitOneColumn) {
|
||||||
|
if (extendGap.rightOrBottom)
|
||||||
|
return Constants.thumbnailSize
|
||||||
|
if (extendGap.leftOrTop)
|
||||||
|
return -root.innerGridSpacing
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return -root.extend
|
||||||
|
}
|
||||||
|
width: extendGap.portraitOneColumn ? Constants.thumbnailSize + 2
|
||||||
|
* root.extend : root.innerGridSpacing
|
||||||
|
height: extendGap.portraitOneColumn ? root.innerGridSpacing : Constants.thumbnailSize
|
||||||
|
+ 2 * root.extend
|
||||||
|
color: StudioTheme.Values.themeStateHighlight
|
||||||
|
visible: extendBackground.radius !== 0
|
||||||
|
&& extendBackground.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
StateThumbnail {
|
||||||
|
id: stateThumbnail
|
||||||
|
width: Constants.thumbnailSize
|
||||||
|
height: Constants.thumbnailSize
|
||||||
|
visualIndex: delegateRoot.visualIndex
|
||||||
|
internalNodeId: delegateRoot.internalNodeId
|
||||||
|
isTiny: root.tinyMode
|
||||||
|
|
||||||
|
hasExtend: delegateRoot.hasExtend
|
||||||
|
extendString: delegateRoot.extendString
|
||||||
|
extendedState: statesEditorModel.extendedStates.includes(
|
||||||
|
delegateRoot.stateName)
|
||||||
|
|
||||||
|
hasWhenCondition: delegateRoot.hasWhenCondition
|
||||||
|
|
||||||
|
// Fix ScrollView taking over the dragging event
|
||||||
|
onGrabbing: {
|
||||||
|
frame.interactive = false
|
||||||
|
statesRepeater.grabIndex = stateThumbnail.visualIndex
|
||||||
|
}
|
||||||
|
onLetGo: frame.interactive = true
|
||||||
|
|
||||||
|
// Fix for ScrollView clipping while dragging of StateThumbnail
|
||||||
|
onDragActiveChanged: {
|
||||||
|
if (stateThumbnail.dragActive)
|
||||||
|
parent = scrollViewWrapper
|
||||||
|
else
|
||||||
|
parent = delegateRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
stateName: delegateRoot.stateName
|
||||||
|
thumbnailImageSource: delegateRoot.stateImageSource
|
||||||
|
whenCondition: delegateRoot.whenConditionString
|
||||||
|
|
||||||
|
baseState: !delegateRoot.internalNodeId
|
||||||
|
defaultChecked: delegateRoot.isDefault
|
||||||
|
isChecked: root.currentStateInternalId === delegateRoot.internalNodeId
|
||||||
|
|
||||||
|
onFocusSignal: root.currentStateInternalId = delegateRoot.internalNodeId
|
||||||
|
onDefaultClicked: statesEditorModel.setStateAsDefault(
|
||||||
|
delegateRoot.internalNodeId)
|
||||||
|
|
||||||
|
onClone: root.cloneState(delegateRoot.internalNodeId)
|
||||||
|
onExtend: root.extendState(delegateRoot.internalNodeId)
|
||||||
|
onRemove: root.deleteState(delegateRoot.internalNodeId)
|
||||||
|
|
||||||
|
onStateNameFinished: statesEditorModel.renameState(
|
||||||
|
delegateRoot.internalNodeId,
|
||||||
|
stateThumbnail.stateName)
|
||||||
|
|
||||||
|
onWhenConditionFinished: statesEditorModel.setWhenCondition(
|
||||||
|
delegateRoot.internalNodeId,
|
||||||
|
stateThumbnail.whenCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: addWrapper
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: addCanvas
|
||||||
|
width: root.thumbWidth
|
||||||
|
height: root.thumbHeight
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
|
||||||
|
ctx.strokeStyle = StudioTheme.Values.themeStateHighlight
|
||||||
|
ctx.lineWidth = 6
|
||||||
|
|
||||||
|
var plusExtend = 20
|
||||||
|
var halfWidth = addCanvas.width / 2
|
||||||
|
var halfHeight = addCanvas.height / 2
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(halfWidth, halfHeight - plusExtend)
|
||||||
|
ctx.lineTo(halfWidth, halfHeight + plusExtend)
|
||||||
|
|
||||||
|
ctx.moveTo(halfWidth - plusExtend, halfHeight)
|
||||||
|
ctx.lineTo(halfWidth + plusExtend, halfHeight)
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
ctx.setLineDash([2, 2])
|
||||||
|
ctx.strokeRect(0, 0, addCanvas.width, addCanvas.height)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: addMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: root.createNewState()
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// temporary hover indicator for add button
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.1
|
||||||
|
color: addMouseArea.containsMouse ? "#ffffff" : "#000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml
Normal file
134
share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
import StudioControls 1.0 as StudioControls
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: 25
|
||||||
|
height: 25
|
||||||
|
|
||||||
|
property bool hovered: mouseArea.containsMouse
|
||||||
|
property bool checked: false
|
||||||
|
signal pressed()
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
color: "transparent"
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burger menu icon
|
||||||
|
Column {
|
||||||
|
id: menuIcon
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
|
property color iconColor: StudioTheme.Values.themeTextColor
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectangle
|
||||||
|
width: 19
|
||||||
|
height: 3
|
||||||
|
color: menuIcon.iconColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectangle1
|
||||||
|
width: 19
|
||||||
|
height: 3
|
||||||
|
color: menuIcon.iconColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectangle2
|
||||||
|
width: 19
|
||||||
|
height: 3
|
||||||
|
color: menuIcon.iconColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: root.pressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: !root.hovered && !root.checked
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: menuIcon
|
||||||
|
iconColor: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: root.hovered && !root.checked
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: menuIcon
|
||||||
|
iconColor: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "checked"
|
||||||
|
when: !root.hovered && root.checked
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: menuIcon
|
||||||
|
iconColor: StudioTheme.Values.themeInteraction
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hoverChecked"
|
||||||
|
when: root.hovered && root.checked
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: menuIcon
|
||||||
|
iconColor: StudioTheme.Values.themeInteraction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
108
share/qtcreator/qmldesigner/newstateseditor/StateMenu.qml
Normal file
108
share/qtcreator/qmldesigner/newstateseditor/StateMenu.qml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import StudioTheme as StudioTheme
|
||||||
|
import StudioControls as StudioControls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
StudioControls.Menu {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool isBaseState: false
|
||||||
|
property bool isTiny: false
|
||||||
|
property bool hasExtend: false
|
||||||
|
property bool propertyChangesVisible: false
|
||||||
|
property bool hasAnnotation: false
|
||||||
|
property bool hasWhenCondition: false
|
||||||
|
|
||||||
|
signal clone()
|
||||||
|
signal extend()
|
||||||
|
signal remove()
|
||||||
|
signal toggle()
|
||||||
|
signal resetWhenCondition()
|
||||||
|
signal editAnnotation()
|
||||||
|
signal removeAnnotation()
|
||||||
|
|
||||||
|
closePolicy: Popup.CloseOnReleaseOutside | Popup.CloseOnEscape
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
id: clone
|
||||||
|
visible: !root.isBaseState
|
||||||
|
text: qsTr("Clone")
|
||||||
|
height: clone.visible ? clone.implicitHeight : 0
|
||||||
|
onTriggered: root.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
id: deleteState
|
||||||
|
visible: !root.isBaseState
|
||||||
|
text: qsTr("Delete")
|
||||||
|
height: deleteState.visible ? deleteState.implicitHeight : 0
|
||||||
|
onTriggered: root.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
id: showChanges
|
||||||
|
visible: !root.isBaseState
|
||||||
|
enabled: !root.isTiny
|
||||||
|
text: root.propertyChangesVisible ? qsTr("Show Thumbnail") : qsTr("Show Changes")
|
||||||
|
height: showChanges.visible ? showChanges.implicitHeight : 0
|
||||||
|
onTriggered: root.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
id: extend
|
||||||
|
visible: !root.isBaseState
|
||||||
|
text: qsTr("Extend")
|
||||||
|
height: extend.visible ? extend.implicitHeight : 0
|
||||||
|
onTriggered: root.extend()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuSeparator {}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
enabled: !root.isBaseState && root.hasWhenCondition
|
||||||
|
text: qsTr("Reset when Condition")
|
||||||
|
onTriggered: {
|
||||||
|
statesEditorModel.resetWhenCondition(internalNodeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuSeparator {}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
enabled: !root.isBaseState
|
||||||
|
text: root.hasAnnotation ? qsTr("Edit Annotation") : qsTr("Add Annotation")
|
||||||
|
onTriggered: root.editAnnotation()
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.MenuItem {
|
||||||
|
enabled: !isBaseState && hasAnnotation
|
||||||
|
text: qsTr("Remove Annotation")
|
||||||
|
onTriggered: root.removeAnnotation()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,66 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
ScrollBar {
|
||||||
|
id: scrollBar
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
implicitWidth: scrollBar.interactive ? 6 : 2
|
||||||
|
implicitHeight: scrollBar.interactive ? 6 : 2
|
||||||
|
radius: width / 2
|
||||||
|
opacity: 0.0
|
||||||
|
color: scrollBar.pressed ? StudioTheme.Values.themeSliderActiveTrackHover
|
||||||
|
: StudioTheme.Values.themeSliderHandle
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "active"
|
||||||
|
when: scrollBar.policy === ScrollBar.AlwaysOn
|
||||||
|
|| (scrollBar.active && scrollBar.size < 1.0)
|
||||||
|
PropertyChanges {
|
||||||
|
target: scrollBar.contentItem
|
||||||
|
opacity: 0.75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
from: "active"
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 450
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: scrollBar.contentItem
|
||||||
|
duration: 200
|
||||||
|
property: "opacity"
|
||||||
|
to: 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
746
share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
Normal file
746
share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
import StudioControls 1.0 as StudioControls
|
||||||
|
import QtQuick.Layouts 6.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias thumbnailImageSource: thumbnailImage.source
|
||||||
|
property alias stateName: stateNameField.text
|
||||||
|
property alias whenCondition: whenCondition.text
|
||||||
|
|
||||||
|
property alias defaultChecked: defaultButton.checked
|
||||||
|
property alias menuChecked: menuButton.checked
|
||||||
|
property bool baseState: false
|
||||||
|
property bool isTiny: false
|
||||||
|
property bool propertyChangesVisible: false
|
||||||
|
property bool isChecked: false
|
||||||
|
|
||||||
|
property bool hasExtend: false
|
||||||
|
property string extendString: ""
|
||||||
|
property bool extendedState: false
|
||||||
|
|
||||||
|
property bool hasWhenCondition: false
|
||||||
|
|
||||||
|
property int visualIndex: 0
|
||||||
|
|
||||||
|
property int internalNodeId
|
||||||
|
|
||||||
|
signal focusSignal
|
||||||
|
signal defaultClicked
|
||||||
|
signal clone
|
||||||
|
signal extend
|
||||||
|
signal remove
|
||||||
|
signal stateNameFinished
|
||||||
|
signal whenConditionFinished
|
||||||
|
|
||||||
|
signal grabbing
|
||||||
|
signal letGo
|
||||||
|
|
||||||
|
property alias dragActive: dragHandler.active
|
||||||
|
|
||||||
|
function checkAnnotation() {
|
||||||
|
return statesEditorModel.hasAnnotation(root.internalNodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onIsTinyChanged: {
|
||||||
|
if (root.isTiny) {
|
||||||
|
buttonGrid.rows = 2
|
||||||
|
buttonGrid.columns = 1
|
||||||
|
} else {
|
||||||
|
buttonGrid.columns = 2
|
||||||
|
buttonGrid.rows = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
enabled: !root.baseState && !root.extendedState
|
||||||
|
onGrabChanged: function (transition, point) {
|
||||||
|
if (transition === PointerDevice.GrabPassive
|
||||||
|
|| transition === PointerDevice.GrabExclusive)
|
||||||
|
root.grabbing()
|
||||||
|
|
||||||
|
if (transition === PointerDevice.UngrabPassive
|
||||||
|
|| transition === PointerDevice.CancelGrabPassive
|
||||||
|
|| transition === PointerDevice.UngrabExclusive
|
||||||
|
|| transition === PointerDevice.CancelGrabExclusive)
|
||||||
|
root.letGo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragActiveChanged: {
|
||||||
|
if (root.dragActive)
|
||||||
|
Drag.start()
|
||||||
|
else
|
||||||
|
Drag.drop()
|
||||||
|
}
|
||||||
|
|
||||||
|
Drag.active: dragHandler.active
|
||||||
|
Drag.source: root
|
||||||
|
Drag.hotSpot.x: root.width * 0.5
|
||||||
|
Drag.hotSpot.y: root.height * 0.5
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: stateBackground
|
||||||
|
color: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
|
border.width: root.isChecked ? 4 : 0
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
readonly property int controlHeight: 25
|
||||||
|
readonly property int thumbPadding: 10
|
||||||
|
readonly property int thumbSpacing: 7
|
||||||
|
|
||||||
|
property int innerWidth: root.width - 2 * stateBackground.thumbPadding
|
||||||
|
property int innerHeight: root.height - 2 * stateBackground.thumbPadding - 2
|
||||||
|
* stateBackground.thumbSpacing - 2 * stateBackground.controlHeight
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: true
|
||||||
|
hoverEnabled: true
|
||||||
|
propagateComposedEvents: true
|
||||||
|
onClicked: root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
padding: stateBackground.thumbPadding
|
||||||
|
spacing: stateBackground.thumbSpacing
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: buttonGrid
|
||||||
|
columns: 2
|
||||||
|
rows: 1
|
||||||
|
spacing: stateBackground.thumbSpacing
|
||||||
|
|
||||||
|
StudioControls.AbstractButton {
|
||||||
|
id: defaultButton
|
||||||
|
width: 50
|
||||||
|
height: stateBackground.controlHeight
|
||||||
|
checkedInverted: true
|
||||||
|
buttonIcon: qsTr("Default")
|
||||||
|
iconFont: StudioTheme.Constants.font
|
||||||
|
onClicked: {
|
||||||
|
root.defaultClicked()
|
||||||
|
root.focusSignal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.TextField {
|
||||||
|
id: stateNameField
|
||||||
|
|
||||||
|
property string previousText
|
||||||
|
|
||||||
|
// This is the width for the "big" state
|
||||||
|
property int bigWidth: stateBackground.innerWidth - 2 * stateBackground.thumbSpacing
|
||||||
|
- defaultButton.width - menuButton.width
|
||||||
|
|
||||||
|
width: root.isTiny ? stateBackground.innerWidth : stateNameField.bigWidth
|
||||||
|
height: stateBackground.controlHeight
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
translationIndicatorVisible: false
|
||||||
|
placeholderText: qsTr("State Name")
|
||||||
|
visible: !root.baseState
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (stateNameField.activeFocus)
|
||||||
|
root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
if (stateNameField.previousText === stateNameField.text)
|
||||||
|
return
|
||||||
|
|
||||||
|
stateNameField.previousText = stateNameField.text
|
||||||
|
root.stateNameFinished()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: stateNameField.previousText = stateNameField.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: baseStateLabel
|
||||||
|
width: root.isTiny ? stateBackground.innerWidth : stateNameField.bigWidth
|
||||||
|
height: stateBackground.controlHeight
|
||||||
|
visible: root.baseState
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("Base State")
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: 5
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: !root.isTiny && !root.propertyChangesVisible
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: stateBackground.innerHeight
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: stateImageBackground
|
||||||
|
source: "images/checkers.png"
|
||||||
|
fillMode: Image.Tile
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: stateImageBackground
|
||||||
|
x: Math.floor(
|
||||||
|
(stateBackground.innerWidth - thumbnailImage.paintedWidth) / 2) - StudioTheme.Values.border
|
||||||
|
y: Math.floor(
|
||||||
|
(stateBackground.innerHeight - thumbnailImage.paintedHeight) / 2) - StudioTheme.Values.border
|
||||||
|
width: Math.round(thumbnailImage.paintedWidth) + 2 * StudioTheme.Values.border
|
||||||
|
height: Math.round(thumbnailImage.paintedHeight) + 2 * StudioTheme.Values.border
|
||||||
|
color: "transparent"
|
||||||
|
border.width: StudioTheme.Values.border
|
||||||
|
border.color: StudioTheme.Values.themeStatePreviewOutline
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: thumbnailImage
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChangesModel {
|
||||||
|
id: propertyChangesModel
|
||||||
|
modelNodeBackendProperty: statesEditorModel.stateModelNode(root.internalNodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
visible: !root.isTiny && root.propertyChangesVisible && !propertyChangesModel.count
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: stateBackground.innerHeight
|
||||||
|
text: qsTr("No Property Changes Available")
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
visible: !root.isTiny && root.propertyChangesVisible && propertyChangesModel.count
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: stateBackground.innerHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ScrollBar.horizontal: StateScrollBar {
|
||||||
|
parent: scrollView
|
||||||
|
x: scrollView.leftPadding
|
||||||
|
y: scrollView.height - height
|
||||||
|
width: scrollView.availableWidth
|
||||||
|
active: scrollView.ScrollBar.vertical.active
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
onPressedChanged: root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: StateScrollBar {
|
||||||
|
parent: scrollView
|
||||||
|
x: scrollView.mirrored ? 0 : scrollView.width - width
|
||||||
|
y: scrollView.topPadding
|
||||||
|
height: scrollView.availableHeight
|
||||||
|
active: scrollView.ScrollBar.horizontal.active
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
onPressedChanged: root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
boundsMovement: Flickable.StopAtBounds
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
interactive: true
|
||||||
|
contentWidth: column.width
|
||||||
|
contentHeight: column.height
|
||||||
|
|
||||||
|
// ScrollView needs an extra TapHandler on top in order to receive click
|
||||||
|
// events. MouseAreas below ScrollView do not let clicks through.
|
||||||
|
TapHandler {
|
||||||
|
id: tapHandler
|
||||||
|
onTapped: root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
// Grid sizes
|
||||||
|
property int gridSpacing: 20
|
||||||
|
property int gridRowSpacing: 5
|
||||||
|
property int gridPadding: 5
|
||||||
|
property int col1Width: 100 // labels
|
||||||
|
property int col2Width: stateBackground.innerWidth - column.gridSpacing - 2
|
||||||
|
* column.gridPadding - column.col1Width // controls
|
||||||
|
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
spacing: stateBackground.thumbSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: propertyChangesModel
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: propertyChanges
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
required property string target
|
||||||
|
required property bool explicit
|
||||||
|
required property bool restoreEntryValues
|
||||||
|
required property var propertyModelNode
|
||||||
|
|
||||||
|
width: column.width
|
||||||
|
height: propertyChangesColumn.height
|
||||||
|
color: StudioTheme.Values.themeBackgroundColorAlternate
|
||||||
|
|
||||||
|
PropertyModel {
|
||||||
|
id: propertyModel
|
||||||
|
modelNodeBackendProperty: propertyChanges.propertyModelNode
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: propertyChangesColumn
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: section
|
||||||
|
property int animationDuration: 120
|
||||||
|
property bool expanded: false
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: Math.round(sectionColumn.height + header.height)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: header
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: StudioTheme.Values.sectionHeadHeight
|
||||||
|
color: StudioTheme.Values.themeSectionHeadBackground
|
||||||
|
|
||||||
|
Row {
|
||||||
|
x: column.gridPadding
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: column.gridSpacing
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("Target")
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
width: column.col1Width
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: propertyChanges.target
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: column.col2Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: arrow
|
||||||
|
width: StudioTheme.Values.spinControlIconSizeMulti
|
||||||
|
height: StudioTheme.Values.spinControlIconSizeMulti
|
||||||
|
text: StudioTheme.Constants.sectionToggle
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
duration: section.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
section.expanded = !section.expanded
|
||||||
|
if (!section.expanded)
|
||||||
|
section.forceActiveFocus()
|
||||||
|
root.focusSignal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: sectionColumn
|
||||||
|
y: header.height
|
||||||
|
padding: column.gridPadding
|
||||||
|
spacing: column.gridRowSpacing
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: column.gridSpacing
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("Explicit")
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: column.col1Width
|
||||||
|
height: explicitSwitch.height
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.Switch {
|
||||||
|
id: explicitSwitch
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
checked: propertyChanges.explicit
|
||||||
|
onToggled: {
|
||||||
|
root.focusSignal()
|
||||||
|
propertyModel.setExplicit(
|
||||||
|
explicitSwitch.checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: column.gridSpacing
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("Restore values")
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
width: column.col1Width
|
||||||
|
height: restoreSwitch.height
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.Switch {
|
||||||
|
id: restoreSwitch
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
checked: propertyChanges.restoreEntryValues
|
||||||
|
onToggled: {
|
||||||
|
root.focusSignal()
|
||||||
|
propertyModel.setRestoreEntryValues(
|
||||||
|
restoreSwitch.checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
duration: section.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "Expanded"
|
||||||
|
when: section.expanded
|
||||||
|
PropertyChanges {
|
||||||
|
target: arrow
|
||||||
|
rotation: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Collapsed"
|
||||||
|
when: !section.expanded
|
||||||
|
PropertyChanges {
|
||||||
|
target: section
|
||||||
|
height: header.height
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: arrow
|
||||||
|
rotation: -90
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: propertyColumn
|
||||||
|
padding: column.gridPadding
|
||||||
|
spacing: column.gridRowSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: propertyModel
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
id: propertyDelegate
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property var value
|
||||||
|
required property string type
|
||||||
|
|
||||||
|
width: stateBackground.innerWidth - 2 * column.gridPadding
|
||||||
|
height: 26
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onClicked: root.focusSignal()
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
border.color: StudioTheme.Values.themeInteraction
|
||||||
|
border.width: propertyDelegate.hovered ? StudioTheme.Values.border : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: removeItem
|
||||||
|
visible: propertyDelegate.hovered
|
||||||
|
x: propertyDelegate.width - removeItem.width
|
||||||
|
z: 10
|
||||||
|
width: removeItem.visible ? propertyDelegate.height : 0
|
||||||
|
height: removeItem.visible ? propertyDelegate.height : 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: removeIcon
|
||||||
|
anchors.fill: parent
|
||||||
|
text: StudioTheme.Constants.closeCross
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: StudioTheme.Values.myIconFontSize
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
scale: propertyDelegateMouseArea.containsMouse ? 1.2 : 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: propertyDelegateMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
root.focusSignal()
|
||||||
|
propertyModel.removeProperty(
|
||||||
|
propertyDelegate.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: column.gridSpacing
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: propertyDelegate.name
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: column.col1Width
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: propertyDelegate.value
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: column.col2Width - removeItem.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingEditor {
|
||||||
|
id: bindingEditor
|
||||||
|
|
||||||
|
property string newWhenCondition
|
||||||
|
|
||||||
|
property Timer timer: Timer {
|
||||||
|
id: timer
|
||||||
|
running: false
|
||||||
|
interval: 50
|
||||||
|
repeat: false
|
||||||
|
onTriggered: statesEditorModel.setWhenCondition(root.internalNodeId,
|
||||||
|
bindingEditor.newWhenCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateModelNodeProperty: statesEditorModel.stateModelNode(root.internalNodeId)
|
||||||
|
stateNameProperty: root.stateName
|
||||||
|
|
||||||
|
onRejected: bindingEditor.hideWidget()
|
||||||
|
onAccepted: {
|
||||||
|
bindingEditor.newWhenCondition = bindingEditor.text.trim()
|
||||||
|
timer.start()
|
||||||
|
bindingEditor.hideWidget()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioControls.TextField {
|
||||||
|
id: whenCondition
|
||||||
|
|
||||||
|
property string previousCondition
|
||||||
|
|
||||||
|
width: stateBackground.innerWidth
|
||||||
|
height: stateBackground.controlHeight
|
||||||
|
|
||||||
|
visible: !root.baseState
|
||||||
|
indicatorVisible: true
|
||||||
|
indicator.icon.text: StudioTheme.Constants.edit
|
||||||
|
indicator.onClicked: {
|
||||||
|
bindingEditor.showWidget()
|
||||||
|
bindingEditor.text = whenCondition.text
|
||||||
|
bindingEditor.prepareBindings()
|
||||||
|
bindingEditor.updateWindowName()
|
||||||
|
}
|
||||||
|
|
||||||
|
actionIndicatorVisible: false
|
||||||
|
translationIndicatorVisible: false
|
||||||
|
placeholderText: qsTr("When Condition")
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (whenCondition.activeFocus)
|
||||||
|
root.focusSignal()
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
if (whenCondition.previousCondition === whenCondition.text)
|
||||||
|
return
|
||||||
|
|
||||||
|
whenCondition.previousCondition = whenCondition.text
|
||||||
|
root.whenConditionFinished()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: whenCondition.previousCondition = whenCondition.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuButton {
|
||||||
|
id: menuButton
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
visible: !root.baseState
|
||||||
|
checked: stateMenu.opened
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
if (!stateMenu.opened)
|
||||||
|
stateMenu.popup()
|
||||||
|
|
||||||
|
root.focusSignal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMenu {
|
||||||
|
id: stateMenu
|
||||||
|
x: 56
|
||||||
|
|
||||||
|
isBaseState: root.baseState
|
||||||
|
isTiny: root.isTiny
|
||||||
|
hasExtend: root.hasExtend
|
||||||
|
propertyChangesVisible: root.propertyChangesVisible
|
||||||
|
hasAnnotation: root.checkAnnotation()
|
||||||
|
hasWhenCondition: root.hasWhenCondition
|
||||||
|
|
||||||
|
onClone: root.clone()
|
||||||
|
onExtend: root.extend()
|
||||||
|
onRemove: root.remove()
|
||||||
|
onToggle: root.propertyChangesVisible = !root.propertyChangesVisible
|
||||||
|
onEditAnnotation: {
|
||||||
|
statesEditorModel.setAnnotation(root.internalNodeId)
|
||||||
|
stateMenu.hasAnnotation = root.checkAnnotation()
|
||||||
|
}
|
||||||
|
onRemoveAnnotation: {
|
||||||
|
statesEditorModel.removeAnnotation(root.internalNodeId)
|
||||||
|
stateMenu.hasAnnotation = root.checkAnnotation()
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: stateMenu.hasAnnotation = root.checkAnnotation()
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool anyControlHovered: defaultButton.hovered || menuButton.hovered
|
||||||
|
|| scrollView.hovered || stateNameField.hover
|
||||||
|
|| whenCondition.hover
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: !mouseArea.containsMouse && !root.anyControlHovered && !dragHandler.active
|
||||||
|
&& !root.baseState
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: stateBackground
|
||||||
|
color: StudioTheme.Values.themeControlBackground
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: (mouseArea.containsMouse || root.anyControlHovered) && !dragHandler.active
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: stateBackground
|
||||||
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "baseState"
|
||||||
|
when: root.baseState && !mouseArea.containsMouse && !root.anyControlHovered
|
||||||
|
&& !dragHandler.active
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: stateBackground
|
||||||
|
color: StudioTheme.Values.themeStateHighlight
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "drag"
|
||||||
|
when: dragHandler.active
|
||||||
|
|
||||||
|
AnchorChanges {
|
||||||
|
target: root
|
||||||
|
anchors.horizontalCenter: undefined
|
||||||
|
anchors.verticalCenter: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
share/qtcreator/qmldesigner/newstateseditor/images/checkers.png
Normal file
BIN
share/qtcreator/qmldesigner/newstateseditor/images/checkers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 B |
@@ -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
|
@@ -43,6 +43,9 @@ T.AbstractButton {
|
|||||||
property alias backgroundVisible: buttonBackground.visible
|
property alias backgroundVisible: buttonBackground.visible
|
||||||
property alias backgroundRadius: buttonBackground.radius
|
property alias backgroundRadius: buttonBackground.radius
|
||||||
|
|
||||||
|
// Inverts the checked style
|
||||||
|
property bool checkedInverted: false
|
||||||
|
|
||||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||||
implicitContentWidth + leftPadding + rightPadding)
|
implicitContentWidth + leftPadding + rightPadding)
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||||
@@ -98,11 +101,12 @@ T.AbstractButton {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "select"
|
name: "check"
|
||||||
when: myButton.enabled && !myButton.pressed && myButton.checked
|
when: myButton.enabled && !myButton.pressed && myButton.checked
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonIcon
|
target: buttonIcon
|
||||||
color: StudioTheme.Values.themeIconColorSelected
|
color: myButton.checkedInverted ? StudioTheme.Values.themeTextSelectedTextColor
|
||||||
|
: StudioTheme.Values.themeIconColorSelected
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
@@ -125,6 +129,7 @@ T.AbstractButton {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: buttonBackground
|
||||||
color: StudioTheme.Values.themeControlBackground
|
color: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
}
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: myButton
|
target: myButton
|
||||||
@@ -137,19 +142,32 @@ T.AbstractButton {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: buttonBackground
|
||||||
color: StudioTheme.Values.themeControlBackground
|
color: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "hover"
|
name: "hover"
|
||||||
when: myButton.hover && !myButton.pressed && myButton.enabled
|
when: !myButton.checked && myButton.hover && !myButton.pressed && myButton.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: buttonBackground
|
||||||
color: StudioTheme.Values.themeControlBackgroundHover
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hoverCheck"
|
||||||
|
when: myButton.checked && myButton.hover && !myButton.pressed && myButton.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: buttonBackground
|
||||||
|
color: myButton.checkedInverted ? StudioTheme.Values.themeInteractionHover
|
||||||
|
: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
border.color: myButton.checkedInverted ? StudioTheme.Values.themeInteractionHover
|
||||||
|
: StudioTheme.Values.themeControlOutline
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "press"
|
name: "press"
|
||||||
when: myButton.hover && myButton.pressed
|
when: myButton.hover && myButton.pressed && myButton.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonBackground
|
target: buttonBackground
|
||||||
color: StudioTheme.Values.themeInteraction
|
color: StudioTheme.Values.themeInteraction
|
||||||
@@ -160,6 +178,17 @@ T.AbstractButton {
|
|||||||
z: 100
|
z: 100
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
State {
|
||||||
|
name: "check"
|
||||||
|
when: myButton.enabled && !myButton.pressed && myButton.checked
|
||||||
|
PropertyChanges {
|
||||||
|
target: buttonBackground
|
||||||
|
color: myButton.checkedInverted ? StudioTheme.Values.themeInteraction
|
||||||
|
: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: myButton.checkedInverted ? StudioTheme.Values.themeInteraction
|
||||||
|
: StudioTheme.Values.themeControlOutline
|
||||||
|
}
|
||||||
|
},
|
||||||
State {
|
State {
|
||||||
name: "disable"
|
name: "disable"
|
||||||
when: !myButton.enabled
|
when: !myButton.enabled
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as C
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
T.Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||||
|
contentWidth + leftPadding + rightPadding,
|
||||||
|
implicitHeaderWidth,
|
||||||
|
implicitFooterWidth)
|
||||||
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||||
|
contentHeight + topPadding + bottomPadding
|
||||||
|
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
|
||||||
|
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
|
||||||
|
|
||||||
|
padding: StudioTheme.Values.dialogPadding
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: StudioTheme.Values.themeDialogBackground
|
||||||
|
border.color: StudioTheme.Values.themeDialogOutline
|
||||||
|
border.width: StudioTheme.Values.border
|
||||||
|
}
|
||||||
|
|
||||||
|
header: T.Label {
|
||||||
|
text: root.title
|
||||||
|
visible: root.title
|
||||||
|
elide: T.Label.ElideRight
|
||||||
|
font.bold: true
|
||||||
|
padding: StudioTheme.Values.dialogPadding
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
x: StudioTheme.Values.border
|
||||||
|
y: StudioTheme.Values.border
|
||||||
|
width: parent.width - (2 * StudioTheme.Values.border)
|
||||||
|
height: parent.height - (2 * StudioTheme.Values.border)
|
||||||
|
color: StudioTheme.Values.themeDialogBackground
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
visible: count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
T.Overlay.modal: Rectangle {
|
||||||
|
color: Qt.alpha(StudioTheme.Values.themeDialogBackground, 0.5)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,105 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
T.Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitWidth: Math.max(
|
||||||
|
background ? background.implicitWidth : 0,
|
||||||
|
textItem.implicitWidth + leftPadding + rightPadding)
|
||||||
|
implicitHeight: Math.max(
|
||||||
|
background ? background.implicitHeight : 0,
|
||||||
|
textItem.implicitHeight + topPadding + bottomPadding)
|
||||||
|
leftPadding: StudioTheme.Values.dialogButtonPadding
|
||||||
|
rightPadding: StudioTheme.Values.dialogButtonPadding
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: background
|
||||||
|
implicitWidth: 70
|
||||||
|
implicitHeight: 20
|
||||||
|
color: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
id: textItem
|
||||||
|
text: root.text
|
||||||
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "default"
|
||||||
|
when: !root.down && !root.hovered && !root.checked
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: root.highlighted ? StudioTheme.Values.themeInteraction
|
||||||
|
: StudioTheme.Values.themeControlBackground
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: textItem
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: root.hovered && !root.checked && !root.down
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
border.color: StudioTheme.Values.themeControlOutline
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: textItem
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "pressed"
|
||||||
|
when: root.checked || root.down
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: background
|
||||||
|
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||||
|
border.color: StudioTheme.Values.themeControlOutlineInteraction
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: textItem
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,69 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as C
|
||||||
|
import QtQuick.Templates as T
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
T.DialogButtonBox {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||||
|
(control.count === 1 ? implicitContentWidth * 2 : implicitContentWidth) + leftPadding + rightPadding)
|
||||||
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||||
|
implicitContentHeight + topPadding + bottomPadding)
|
||||||
|
contentWidth: contentItem.contentWidth
|
||||||
|
|
||||||
|
spacing: StudioTheme.Values.dialogButtonSpacing
|
||||||
|
padding: StudioTheme.Values.dialogPadding
|
||||||
|
alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
|
|
||||||
|
|
||||||
|
delegate: DialogButton {
|
||||||
|
width: control.count === 1 ? control.availableWidth / 2 : undefined
|
||||||
|
implicitHeight: StudioTheme.Values.height
|
||||||
|
highlighted: DialogButtonBox.buttonRole === DialogButtonBox.AcceptRole
|
||||||
|
|| DialogButtonBox.buttonRole === DialogButtonBox.ApplyRole
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: ListView {
|
||||||
|
implicitWidth: contentWidth
|
||||||
|
model: control.contentModel
|
||||||
|
spacing: control.spacing
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: 30
|
||||||
|
x: StudioTheme.Values.border
|
||||||
|
y: StudioTheme.Values.border
|
||||||
|
width: parent.width - (2 * StudioTheme.Values.border)
|
||||||
|
height: parent.height - (2 * StudioTheme.Values.border)
|
||||||
|
color: StudioTheme.Values.themeDialogBackground
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Templates 2.15 as T
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias icon: icon
|
||||||
|
property bool hover: mouseArea.containsMouse
|
||||||
|
property bool pressed: mouseArea.pressed
|
||||||
|
|
||||||
|
implicitWidth: StudioTheme.Values.height
|
||||||
|
implicitHeight: StudioTheme.Values.height
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
T.Label {
|
||||||
|
id: icon
|
||||||
|
anchors.fill: parent
|
||||||
|
text: StudioTheme.Constants.actionIcon
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
font.pixelSize: StudioTheme.Values.myIconFontSize
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hover"
|
||||||
|
when: root.hover && !root.pressed && root.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: icon
|
||||||
|
scale: 1.2
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "disable"
|
||||||
|
when: !root.enabled
|
||||||
|
PropertyChanges {
|
||||||
|
target: icon
|
||||||
|
color: StudioTheme.Values.themeTextColorDisabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
@@ -30,22 +30,24 @@ import StudioTheme 1.0 as StudioTheme
|
|||||||
T.TextField {
|
T.TextField {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias actionIndicator: actionIndicator
|
|
||||||
property alias translationIndicator: translationIndicator
|
|
||||||
|
|
||||||
// This property is used to indicate the global hover state
|
// This property is used to indicate the global hover state
|
||||||
property bool hover: (actionIndicator.hover || mouseArea.containsMouse
|
property bool hover: (actionIndicator.hover || mouseArea.containsMouse || indicator.hover
|
||||||
|| translationIndicator.hover) && root.enabled
|
|| translationIndicator.hover) && root.enabled
|
||||||
property bool edit: root.activeFocus
|
property bool edit: root.activeFocus
|
||||||
|
|
||||||
|
property alias actionIndicator: actionIndicator
|
||||||
property alias actionIndicatorVisible: actionIndicator.visible
|
property alias actionIndicatorVisible: actionIndicator.visible
|
||||||
property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
|
property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
|
||||||
property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
|
property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
|
||||||
|
|
||||||
|
property alias translationIndicator: translationIndicator
|
||||||
property alias translationIndicatorVisible: translationIndicator.visible
|
property alias translationIndicatorVisible: translationIndicator.visible
|
||||||
property real __translationIndicatorWidth: StudioTheme.Values.translationIndicatorWidth
|
property real __translationIndicatorWidth: StudioTheme.Values.translationIndicatorWidth
|
||||||
property real __translationIndicatorHeight: StudioTheme.Values.translationIndicatorHeight
|
property real __translationIndicatorHeight: StudioTheme.Values.translationIndicatorHeight
|
||||||
|
|
||||||
|
property alias indicator: indicator
|
||||||
|
property alias indicatorVisible: indicator.visible
|
||||||
|
|
||||||
property string preFocusText: ""
|
property string preFocusText: ""
|
||||||
|
|
||||||
horizontalAlignment: Qt.AlignLeft
|
horizontalAlignment: Qt.AlignLeft
|
||||||
@@ -68,7 +70,7 @@ T.TextField {
|
|||||||
implicitHeight: StudioTheme.Values.defaultControlHeight
|
implicitHeight: StudioTheme.Values.defaultControlHeight
|
||||||
|
|
||||||
leftPadding: StudioTheme.Values.inputHorizontalPadding + actionIndicator.width
|
leftPadding: StudioTheme.Values.inputHorizontalPadding + actionIndicator.width
|
||||||
rightPadding: StudioTheme.Values.inputHorizontalPadding + translationIndicator.width
|
rightPadding: StudioTheme.Values.inputHorizontalPadding + translationIndicator.width + indicator.width
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
@@ -144,6 +146,14 @@ T.TextField {
|
|||||||
height: root.height
|
height: root.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Indicator {
|
||||||
|
id: indicator
|
||||||
|
visible: false
|
||||||
|
x: root.width - translationIndicator.width - indicator.width
|
||||||
|
width: indicator.visible ? root.height : 0
|
||||||
|
height: indicator.visible ? root.height : 0
|
||||||
|
}
|
||||||
|
|
||||||
TranslationIndicator {
|
TranslationIndicator {
|
||||||
id: translationIndicator
|
id: translationIndicator
|
||||||
myControl: root
|
myControl: root
|
||||||
@@ -173,8 +183,8 @@ T.TextField {
|
|||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "globalHover"
|
name: "globalHover"
|
||||||
when: (actionIndicator.hover || translationIndicator.hover) && !root.edit
|
when: (actionIndicator.hover || translationIndicator.hover || indicator.hover)
|
||||||
&& root.enabled
|
&& !root.edit && root.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: textFieldBackground
|
target: textFieldBackground
|
||||||
color: StudioTheme.Values.themeControlBackgroundGlobalHover
|
color: StudioTheme.Values.themeControlBackgroundGlobalHover
|
||||||
@@ -189,7 +199,7 @@ T.TextField {
|
|||||||
State {
|
State {
|
||||||
name: "hover"
|
name: "hover"
|
||||||
when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover
|
when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover
|
||||||
&& !root.edit && root.enabled
|
&& !indicator.hover && !root.edit && root.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: textFieldBackground
|
target: textFieldBackground
|
||||||
color: StudioTheme.Values.themeControlBackgroundHover
|
color: StudioTheme.Values.themeControlBackgroundHover
|
||||||
|
@@ -8,7 +8,11 @@ CheckIndicator 1.0 CheckIndicator.qml
|
|||||||
ComboBox 1.0 ComboBox.qml
|
ComboBox 1.0 ComboBox.qml
|
||||||
ComboBoxInput 1.0 ComboBoxInput.qml
|
ComboBoxInput 1.0 ComboBoxInput.qml
|
||||||
ContextMenu 1.0 ContextMenu.qml
|
ContextMenu 1.0 ContextMenu.qml
|
||||||
|
Dialog 1.0 Dialog.qml
|
||||||
|
DialogButton 1.0 DialogButton.qml
|
||||||
|
DialogButtonBox 1.0 DialogButtonBox.qml
|
||||||
FilterComboBox 1.0 FilterComboBox.qml
|
FilterComboBox 1.0 FilterComboBox.qml
|
||||||
|
Indicator 1.0 Indicator.qml
|
||||||
InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml
|
InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml
|
||||||
ItemDelegate 1.0 ItemDelegate.qml
|
ItemDelegate 1.0 ItemDelegate.qml
|
||||||
LinkIndicator2D 1.0 LinkIndicator2D.qml
|
LinkIndicator2D 1.0 LinkIndicator2D.qml
|
||||||
|
@@ -216,6 +216,15 @@ QtObject {
|
|||||||
property real colorEditorPopupCmoboBoxWidth: 110
|
property real colorEditorPopupCmoboBoxWidth: 110
|
||||||
property real colorEditorPopupSpinBoxWidth: 54
|
property real colorEditorPopupSpinBoxWidth: 54
|
||||||
|
|
||||||
|
// Toolbar
|
||||||
|
property real toolbarHeight: 35
|
||||||
|
property real toolbarSpacing: 8
|
||||||
|
|
||||||
|
// Dialog
|
||||||
|
property real dialogPadding: 12
|
||||||
|
property real dialogButtonSpacing: 10
|
||||||
|
property real dialogButtonPadding: 4
|
||||||
|
|
||||||
// Theme Colors
|
// Theme Colors
|
||||||
|
|
||||||
property bool isLightTheme: themeControlBackground.hsvValue > themeTextColor.hsvValue
|
property bool isLightTheme: themeControlBackground.hsvValue > themeTextColor.hsvValue
|
||||||
@@ -288,7 +297,7 @@ QtObject {
|
|||||||
|
|
||||||
// Slider colors
|
// Slider colors
|
||||||
property string themeSliderActiveTrack: Theme.color(Theme.DSsliderActiveTrack)
|
property string themeSliderActiveTrack: Theme.color(Theme.DSsliderActiveTrack)
|
||||||
property string themeSliderActiveTrackHover: Theme.color(Theme.DSactiveTrackHover)
|
property string themeSliderActiveTrackHover: Theme.color(Theme.DSsliderActiveTrackHover)
|
||||||
property string themeSliderActiveTrackFocus: Theme.color(Theme.DSsliderActiveTrackFocus)
|
property string themeSliderActiveTrackFocus: Theme.color(Theme.DSsliderActiveTrackFocus)
|
||||||
property string themeSliderInactiveTrack: Theme.color(Theme.DSsliderInactiveTrack)
|
property string themeSliderInactiveTrack: Theme.color(Theme.DSsliderInactiveTrack)
|
||||||
property string themeSliderInactiveTrackHover: Theme.color(Theme.DSsliderInactiveTrackHover)
|
property string themeSliderInactiveTrackHover: Theme.color(Theme.DSsliderInactiveTrackHover)
|
||||||
@@ -308,10 +317,15 @@ QtObject {
|
|||||||
property string themeTabInactiveBackground: Theme.color(Theme.DStabInactiveBackground)
|
property string themeTabInactiveBackground: Theme.color(Theme.DStabInactiveBackground)
|
||||||
property string themeTabInactiveText: Theme.color(Theme.DStabInactiveText)
|
property string themeTabInactiveText: Theme.color(Theme.DStabInactiveText)
|
||||||
|
|
||||||
|
// State Editor
|
||||||
property string themeStateSeparator: Theme.color(Theme.DSstateSeparatorColor)
|
property string themeStateSeparator: Theme.color(Theme.DSstateSeparatorColor)
|
||||||
property string themeStateBackground: Theme.color(Theme.DSstateBackgroundColor)
|
property string themeStateBackground: Theme.color(Theme.DSstateBackgroundColor)
|
||||||
property string themeStatePreviewOutline: Theme.color(Theme.DSstatePreviewOutline)
|
property string themeStatePreviewOutline: Theme.color(Theme.DSstatePreviewOutline)
|
||||||
|
|
||||||
|
// State Editor *new*
|
||||||
|
property color themeStatePanelBackground: Theme.color(Theme.DSstatePanelBackground)
|
||||||
|
property color themeStateHighlight: Theme.color(Theme.DSstateHighlight)
|
||||||
|
|
||||||
property string themeUnimportedModuleColor: Theme.color(Theme.DSUnimportedModuleColor)
|
property string themeUnimportedModuleColor: Theme.color(Theme.DSUnimportedModuleColor)
|
||||||
|
|
||||||
// Taken out of Constants.js
|
// Taken out of Constants.js
|
||||||
@@ -334,9 +348,13 @@ QtObject {
|
|||||||
property string themeListItemTextHover: Theme.color(Theme.DSnavigatorTextHover)
|
property string themeListItemTextHover: Theme.color(Theme.DSnavigatorTextHover)
|
||||||
property string themeListItemTextPress: Theme.color(Theme.DSnavigatorTextSelected)
|
property string themeListItemTextPress: Theme.color(Theme.DSnavigatorTextSelected)
|
||||||
|
|
||||||
//Welcome Page
|
// Welcome Page
|
||||||
property string welcomeScreenBackground: Theme.color(Theme.DSwelcomeScreenBackground)
|
property string welcomeScreenBackground: Theme.color(Theme.DSwelcomeScreenBackground)
|
||||||
property string themeSubPanelBackground: Theme.color(Theme.DSsubPanelBackground)
|
property string themeSubPanelBackground: Theme.color(Theme.DSsubPanelBackground)
|
||||||
property string themeThumbnailBackground: Theme.color(Theme.DSthumbnailBackground)
|
property string themeThumbnailBackground: Theme.color(Theme.DSthumbnailBackground)
|
||||||
property string themeThumbnailLabelBackground: Theme.color(Theme.DSthumbnailLabelBackground)
|
property string themeThumbnailLabelBackground: Theme.color(Theme.DSthumbnailLabelBackground)
|
||||||
|
|
||||||
|
// Dialog
|
||||||
|
property color themeDialogBackground: values.themeThumbnailBackground
|
||||||
|
property color themeDialogOutline: values.themeInteraction
|
||||||
}
|
}
|
||||||
|
@@ -94,6 +94,9 @@ DSstateSeparatorColor=ff7c7b7b
|
|||||||
DSstateBackgroundColor=ff383838
|
DSstateBackgroundColor=ff383838
|
||||||
DSstatePreviewOutline=ffaaaaaa
|
DSstatePreviewOutline=ffaaaaaa
|
||||||
|
|
||||||
|
DSstatePanelBackground=ff252525
|
||||||
|
DSstateHighlight=ff727272
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -85,6 +85,9 @@ DSstateSeparatorColor=ffadadad
|
|||||||
DSstateBackgroundColor=ffe0e0e0
|
DSstateBackgroundColor=ffe0e0e0
|
||||||
DSstatePreviewOutline=ff363636
|
DSstatePreviewOutline=ff363636
|
||||||
|
|
||||||
|
DSstatePanelBackground=ffdadada
|
||||||
|
DSstateHighlight=ff8d8d8d
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -99,6 +99,9 @@ DSstateSeparatorColor=ffadadad
|
|||||||
DSstateBackgroundColor=ffe0e0e0
|
DSstateBackgroundColor=ffe0e0e0
|
||||||
DSstatePreviewOutline=ff363636
|
DSstatePreviewOutline=ff363636
|
||||||
|
|
||||||
|
DSstatePanelBackground=ffdadada
|
||||||
|
DSstateHighlight=ff8d8d8d
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -96,6 +96,9 @@ DSstateSeparatorColor=ff7c7b7b
|
|||||||
DSstateBackgroundColor=ff383838
|
DSstateBackgroundColor=ff383838
|
||||||
DSstatePreviewOutline=ffaaaaaa
|
DSstatePreviewOutline=ffaaaaaa
|
||||||
|
|
||||||
|
DSstatePanelBackground=ff252525
|
||||||
|
DSstateHighlight=ff727272
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -98,6 +98,9 @@ DSstateSeparatorColor=ff7c7b7b
|
|||||||
DSstateBackgroundColor=ff383838
|
DSstateBackgroundColor=ff383838
|
||||||
DSstatePreviewOutline=ffaaaaaa
|
DSstatePreviewOutline=ffaaaaaa
|
||||||
|
|
||||||
|
DSstatePanelBackground=ff252525
|
||||||
|
DSstateHighlight=ff727272
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -94,6 +94,9 @@ DSstateSeparatorColor=ffadadad
|
|||||||
DSstateBackgroundColor=ffe0e0e0
|
DSstateBackgroundColor=ffe0e0e0
|
||||||
DSstatePreviewOutline=ff363636
|
DSstatePreviewOutline=ff363636
|
||||||
|
|
||||||
|
DSstatePanelBackground=ffdadada
|
||||||
|
DSstateHighlight=ff8d8d8d
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -92,6 +92,9 @@ DSstateSeparatorColor=ff7c7b7b
|
|||||||
DSstateBackgroundColor=ff383838
|
DSstateBackgroundColor=ff383838
|
||||||
DSstatePreviewOutline=ffaaaaaa
|
DSstatePreviewOutline=ffaaaaaa
|
||||||
|
|
||||||
|
DSstatePanelBackground=ff252525
|
||||||
|
DSstateHighlight=ff727272
|
||||||
|
|
||||||
DSchangedStateText=ff99ccff
|
DSchangedStateText=ff99ccff
|
||||||
|
|
||||||
DS3DAxisXColor=ffd00000
|
DS3DAxisXColor=ffd00000
|
||||||
|
@@ -438,6 +438,9 @@ public:
|
|||||||
DSgreenLight,
|
DSgreenLight,
|
||||||
DSamberLight,
|
DSamberLight,
|
||||||
DSredLight,
|
DSredLight,
|
||||||
|
|
||||||
|
DSstatePanelBackground,
|
||||||
|
DSstateHighlight,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Gradient {
|
enum Gradient {
|
||||||
|
@@ -353,6 +353,17 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
stateseditorwidget.cpp stateseditorwidget.h
|
stateseditorwidget.cpp stateseditorwidget.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
SOURCES_PREFIX components/stateseditornew
|
||||||
|
SOURCES
|
||||||
|
propertychangesmodel.cpp propertychangesmodel.h
|
||||||
|
propertymodel.cpp propertymodel.h
|
||||||
|
stateseditorimageprovider.cpp stateseditorimageprovider.h
|
||||||
|
stateseditormodel.cpp stateseditormodel.h
|
||||||
|
stateseditorview.cpp stateseditorview.h
|
||||||
|
stateseditorwidget.cpp stateseditorwidget.h
|
||||||
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
SOURCES_PREFIX components/texteditor
|
SOURCES_PREFIX components/texteditor
|
||||||
SOURCES
|
SOURCES
|
||||||
|
@@ -36,10 +36,12 @@
|
|||||||
#include "gradientpresetcustomlistmodel.h"
|
#include "gradientpresetcustomlistmodel.h"
|
||||||
#include "gradientpresetdefaultlistmodel.h"
|
#include "gradientpresetdefaultlistmodel.h"
|
||||||
#include "itemfiltermodel.h"
|
#include "itemfiltermodel.h"
|
||||||
|
#include "propertychangesmodel.h"
|
||||||
#include "propertyeditorcontextobject.h"
|
#include "propertyeditorcontextobject.h"
|
||||||
#include "propertyeditorimageprovider.h"
|
#include "propertyeditorimageprovider.h"
|
||||||
#include "propertyeditorqmlbackend.h"
|
#include "propertyeditorqmlbackend.h"
|
||||||
#include "propertyeditorvalue.h"
|
#include "propertyeditorvalue.h"
|
||||||
|
#include "propertymodel.h"
|
||||||
#include "qmlanchorbindingproxy.h"
|
#include "qmlanchorbindingproxy.h"
|
||||||
#include "richtexteditor/richtexteditorproxy.h"
|
#include "richtexteditor/richtexteditorproxy.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
@@ -78,6 +80,8 @@ void Quick2PropertyEditorView::registerQmlTypes()
|
|||||||
RichTextEditorProxy::registerDeclarativeType();
|
RichTextEditorProxy::registerDeclarativeType();
|
||||||
SelectionDynamicPropertiesProxyModel::registerDeclarativeType();
|
SelectionDynamicPropertiesProxyModel::registerDeclarativeType();
|
||||||
DynamicPropertyRow::registerDeclarativeType();
|
DynamicPropertyRow::registerDeclarativeType();
|
||||||
|
Experimental::PropertyChangesModel::registerDeclarativeType();
|
||||||
|
Experimental::PropertyModel::registerDeclarativeType();
|
||||||
|
|
||||||
const QString resourcePath = PropertyEditorQmlBackend::propertyEditorResourcesPath();
|
const QString resourcePath = PropertyEditorQmlBackend::propertyEditorResourcesPath();
|
||||||
|
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "propertychangesmodel.h"
|
||||||
|
|
||||||
|
#include "stateseditorview.h"
|
||||||
|
#include <qmlmodelnodeproxy.h>
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QtQml>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
debug = false
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
PropertyChangesModel::PropertyChangesModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PropertyChangesModel::~PropertyChangesModel()
|
||||||
|
{
|
||||||
|
if (m_view)
|
||||||
|
m_view->deregisterPropertyChangesModel(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PropertyChangesModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QmlModelState modelState(m_modelNode);
|
||||||
|
|
||||||
|
if (!modelState.isValid() || modelState.isBaseState())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return modelState.propertyChanges().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PropertyChangesModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.parent().isValid() || index.column() != 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QmlModelState modelState(m_modelNode);
|
||||||
|
if (!modelState.isValid() || modelState.isBaseState())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QList<QmlPropertyChanges> propertyChanges = modelState.propertyChanges();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Target: {
|
||||||
|
const ModelNode target = propertyChanges.at(index.row()).target();
|
||||||
|
if (target.isValid())
|
||||||
|
return target.displayName();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
case Explicit: {
|
||||||
|
return propertyChanges.at(index.row()).explicitValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
case RestoreEntryValues: {
|
||||||
|
return propertyChanges.at(index.row()).restoreEntryValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
case PropertyModelNode: {
|
||||||
|
return propertyChanges.at(index.row()).modelNode().toVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> PropertyChangesModel::roleNames() const
|
||||||
|
{
|
||||||
|
static QHash<int, QByteArray> roleNames{{Target, "target"},
|
||||||
|
{Explicit, "explicit"},
|
||||||
|
{RestoreEntryValues, "restoreEntryValues"},
|
||||||
|
{PropertyModelNode, "propertyModelNode"}};
|
||||||
|
return roleNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyChangesModel::setModelNodeBackend(const QVariant &modelNodeBackend)
|
||||||
|
{
|
||||||
|
ModelNode modelNode = modelNodeBackend.value<ModelNode>();
|
||||||
|
|
||||||
|
if (!modelNode.isValid() || modelNode.isRootNode())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_modelNode = modelNode;
|
||||||
|
|
||||||
|
QTC_ASSERT(m_modelNode.simplifiedTypeName() == "State", return );
|
||||||
|
|
||||||
|
m_view = qobject_cast<StatesEditorView *>(m_modelNode.view());
|
||||||
|
if (m_view)
|
||||||
|
m_view->registerPropertyChangesModel(this);
|
||||||
|
|
||||||
|
emit modelNodeBackendChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyChangesModel::reset()
|
||||||
|
{
|
||||||
|
QAbstractListModel::beginResetModel();
|
||||||
|
QAbstractListModel::endResetModel();
|
||||||
|
|
||||||
|
emit countChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PropertyChangesModel::count() const
|
||||||
|
{
|
||||||
|
return rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyChangesModel::registerDeclarativeType()
|
||||||
|
{
|
||||||
|
qmlRegisterType<PropertyChangesModel>("HelperWidgets", 2, 0, "PropertyChangesModel");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PropertyChangesModel::modelNodeBackend() const
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,80 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
#include <modelnode.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
class StatesEditorView;
|
||||||
|
|
||||||
|
class PropertyChangesModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend
|
||||||
|
NOTIFY modelNodeBackendChanged)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Target = Qt::DisplayRole,
|
||||||
|
Explicit = Qt::UserRole,
|
||||||
|
RestoreEntryValues,
|
||||||
|
PropertyModelNode
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertyChangesModel(QObject *parent = nullptr);
|
||||||
|
~PropertyChangesModel();
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
void setModelNodeBackend(const QVariant &modelNodeBackend);
|
||||||
|
void reset();
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
static void registerDeclarativeType();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modelNodeBackendChanged();
|
||||||
|
void countChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant modelNodeBackend() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModelNode m_modelNode;
|
||||||
|
QPointer<StatesEditorView> m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,172 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "propertymodel.h"
|
||||||
|
|
||||||
|
#include "abstractproperty.h"
|
||||||
|
#include "abstractview.h"
|
||||||
|
#include "bindingproperty.h"
|
||||||
|
#include "nodemetainfo.h"
|
||||||
|
#include "variantproperty.h"
|
||||||
|
|
||||||
|
#include <qmlmodelnodeproxy.h>
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QtQml>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
debug = false
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
PropertyModel::PropertyModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int PropertyModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_properties.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PropertyModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.parent().isValid() || index.column() != 0)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Name: {
|
||||||
|
return m_properties.at(index.row()).name();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Value: {
|
||||||
|
AbstractProperty property = m_properties.at(index.row());
|
||||||
|
|
||||||
|
if (property.isBindingProperty())
|
||||||
|
return property.toBindingProperty().expression();
|
||||||
|
|
||||||
|
if (property.isVariantProperty())
|
||||||
|
return property.toVariantProperty().value();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type: {
|
||||||
|
QmlPropertyChanges propertyChanges(m_modelNode);
|
||||||
|
if (!propertyChanges.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!propertyChanges.target().isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return propertyChanges.target().metaInfo().propertyTypeName(
|
||||||
|
m_properties.at(index.row()).name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> PropertyModel::roleNames() const
|
||||||
|
{
|
||||||
|
static QHash<int, QByteArray> roleNames{{Name, "name"}, {Value, "value"}, {Type, "type"}};
|
||||||
|
return roleNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::setModelNodeBackend(const QVariant &modelNodeBackend)
|
||||||
|
{
|
||||||
|
ModelNode modelNode = modelNodeBackend.value<ModelNode>();
|
||||||
|
|
||||||
|
if (!modelNode.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_modelNode = modelNode;
|
||||||
|
|
||||||
|
QTC_ASSERT(m_modelNode.simplifiedTypeName() == "PropertyChanges", return );
|
||||||
|
|
||||||
|
setupModel();
|
||||||
|
emit modelNodeBackendChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::setExplicit(bool value)
|
||||||
|
{
|
||||||
|
if (!m_modelNode.isValid() || !m_modelNode.view()->isAttached())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlPropertyChanges propertyChanges(m_modelNode);
|
||||||
|
|
||||||
|
if (propertyChanges.isValid())
|
||||||
|
propertyChanges.setExplicitValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::setRestoreEntryValues(bool value)
|
||||||
|
{
|
||||||
|
if (!m_modelNode.isValid() || !m_modelNode.view()->isAttached())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlPropertyChanges propertyChanges(m_modelNode);
|
||||||
|
|
||||||
|
if (propertyChanges.isValid())
|
||||||
|
propertyChanges.setRestoreEntryValues(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::removeProperty(const QString &name)
|
||||||
|
{
|
||||||
|
if (!m_modelNode.isValid() || !m_modelNode.view()->isAttached())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_modelNode.removeProperty(name.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::registerDeclarativeType()
|
||||||
|
{
|
||||||
|
qmlRegisterType<PropertyModel>("HelperWidgets", 2, 0, "PropertyModel");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PropertyModel::modelNodeBackend() const
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyModel::setupModel()
|
||||||
|
{
|
||||||
|
if (!m_modelNode.isValid() || !m_modelNode.view()->isAttached())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlPropertyChanges propertyChanges(m_modelNode);
|
||||||
|
if (!propertyChanges.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_properties = propertyChanges.targetProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,73 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
#include <modelnode.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
class PropertyModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend
|
||||||
|
NOTIFY modelNodeBackendChanged)
|
||||||
|
|
||||||
|
enum { Name = Qt::DisplayRole, Value = Qt::UserRole, Type };
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertyModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
void setModelNodeBackend(const QVariant &modelNodeBackend);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setExplicit(bool value);
|
||||||
|
Q_INVOKABLE void setRestoreEntryValues(bool value);
|
||||||
|
Q_INVOKABLE void removeProperty(const QString &name);
|
||||||
|
|
||||||
|
static void registerDeclarativeType();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modelNodeBackendChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant modelNodeBackend() const;
|
||||||
|
void setupModel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModelNode m_modelNode;
|
||||||
|
QList<AbstractProperty> m_properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,82 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "stateseditorimageprovider.h"
|
||||||
|
#include "nodeinstanceview.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
StatesEditorImageProvider::StatesEditorImageProvider()
|
||||||
|
: QQuickImageProvider(QQuickImageProvider::Image)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage StatesEditorImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
|
||||||
|
{
|
||||||
|
QImage image;
|
||||||
|
|
||||||
|
bool nodeInstanceViewIsDetached = m_nodeInstanceView.isNull() || !m_nodeInstanceView->model();
|
||||||
|
if (!nodeInstanceViewIsDetached) {
|
||||||
|
QString imageId = id.split(QLatin1Char('-')).constFirst();
|
||||||
|
if (imageId == QLatin1String("baseState")) {
|
||||||
|
image = m_nodeInstanceView->statePreviewImage(m_nodeInstanceView->rootModelNode());
|
||||||
|
} else {
|
||||||
|
bool canBeConverted;
|
||||||
|
int instanceId = imageId.toInt(&canBeConverted);
|
||||||
|
if (canBeConverted && m_nodeInstanceView->hasModelNodeForInternalId(instanceId)) {
|
||||||
|
image = m_nodeInstanceView->statePreviewImage(m_nodeInstanceView->modelNodeForInternalId(instanceId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.isNull()) {
|
||||||
|
//creating white QImage
|
||||||
|
QSize newSize = requestedSize;
|
||||||
|
if (newSize.isEmpty())
|
||||||
|
newSize = QSize (100, 100);
|
||||||
|
|
||||||
|
QImage image(newSize, QImage::Format_ARGB32);
|
||||||
|
image.fill(0xFFFFFFFF);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = image.size();
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorImageProvider::setNodeInstanceView(NodeInstanceView *nodeInstanceView)
|
||||||
|
{
|
||||||
|
m_nodeInstanceView = nodeInstanceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
||||||
|
|
@@ -0,0 +1,54 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include"abstractview.h"
|
||||||
|
|
||||||
|
#include <QQuickImageProvider>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class StatesEditorView;
|
||||||
|
|
||||||
|
class StatesEditorImageProvider : public QQuickImageProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StatesEditorImageProvider();
|
||||||
|
|
||||||
|
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
|
||||||
|
|
||||||
|
void setNodeInstanceView(NodeInstanceView *nodeInstanceView);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<NodeInstanceView> m_nodeInstanceView;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,455 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "stateseditormodel.h"
|
||||||
|
#include "stateseditorview.h"
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <modelnode.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
#include <rewriterview.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
debug = false
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
StatesEditorModel::StatesEditorModel(StatesEditorView *view)
|
||||||
|
: QAbstractListModel(view)
|
||||||
|
, m_statesEditorView(view)
|
||||||
|
, m_hasExtend(false)
|
||||||
|
, m_extendedStates()
|
||||||
|
{
|
||||||
|
QObject::connect(this, &StatesEditorModel::dataChanged, [this]() { emit baseStateChanged(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
int StatesEditorModel::count() const
|
||||||
|
{
|
||||||
|
return rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex StatesEditorModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (m_statesEditorView.isNull())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int internalNodeId = 0;
|
||||||
|
if (row > 0)
|
||||||
|
internalNodeId = m_statesEditorView->activeStatesGroupNode()
|
||||||
|
.nodeListProperty("states")
|
||||||
|
.at(row - 1)
|
||||||
|
.internalId();
|
||||||
|
|
||||||
|
return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int StatesEditorModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid() || m_statesEditorView.isNull() || !m_statesEditorView->model())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!m_statesEditorView->activeStatesGroupNode().hasNodeListProperty("states"))
|
||||||
|
return 1; // base state
|
||||||
|
|
||||||
|
return m_statesEditorView->activeStatesGroupNode().nodeListProperty("states").count() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::reset()
|
||||||
|
{
|
||||||
|
QAbstractListModel::beginResetModel();
|
||||||
|
QAbstractListModel::endResetModel();
|
||||||
|
|
||||||
|
evaluateExtend();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.parent().isValid() || index.column() != 0 || m_statesEditorView.isNull()
|
||||||
|
|| !m_statesEditorView->hasModelNodeForInternalId(index.internalId()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
ModelNode stateNode;
|
||||||
|
|
||||||
|
if (index.internalId() > 0)
|
||||||
|
stateNode = m_statesEditorView->modelNodeForInternalId(index.internalId());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case StateNameRole: {
|
||||||
|
if (index.row() == 0) {
|
||||||
|
return tr("base state", "Implicit default state");
|
||||||
|
} else {
|
||||||
|
if (stateNode.hasVariantProperty("name"))
|
||||||
|
return stateNode.variantProperty("name").value();
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case StateImageSourceRole: {
|
||||||
|
static int randomNumber = 0;
|
||||||
|
randomNumber++;
|
||||||
|
if (index.row() == 0)
|
||||||
|
return QString("image://qmldesigner_stateseditor/baseState-%1").arg(randomNumber);
|
||||||
|
else
|
||||||
|
return QString("image://qmldesigner_stateseditor/%1-%2").arg(index.internalId()).arg(randomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
case InternalNodeId:
|
||||||
|
return index.internalId();
|
||||||
|
|
||||||
|
case HasWhenCondition:
|
||||||
|
return stateNode.isValid() && stateNode.hasProperty("when");
|
||||||
|
|
||||||
|
case WhenConditionString: {
|
||||||
|
if (stateNode.isValid() && stateNode.hasBindingProperty("when"))
|
||||||
|
return stateNode.bindingProperty("when").expression();
|
||||||
|
else
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
case IsDefault: {
|
||||||
|
QmlModelState modelState(stateNode);
|
||||||
|
if (modelState.isValid())
|
||||||
|
return modelState.isDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ModelHasDefaultState:
|
||||||
|
return hasDefaultState();
|
||||||
|
|
||||||
|
case HasExtend:
|
||||||
|
return stateNode.isValid() && stateNode.hasProperty("extend");
|
||||||
|
|
||||||
|
case ExtendString: {
|
||||||
|
if (stateNode.isValid() && stateNode.hasVariantProperty("extend"))
|
||||||
|
return stateNode.variantProperty("extend").value();
|
||||||
|
else
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> StatesEditorModel::roleNames() const
|
||||||
|
{
|
||||||
|
static QHash<int, QByteArray> roleNames{{StateNameRole, "stateName"},
|
||||||
|
{StateImageSourceRole, "stateImageSource"},
|
||||||
|
{InternalNodeId, "internalNodeId"},
|
||||||
|
{HasWhenCondition, "hasWhenCondition"},
|
||||||
|
{WhenConditionString, "whenConditionString"},
|
||||||
|
{IsDefault, "isDefault"},
|
||||||
|
{ModelHasDefaultState, "modelHasDefaultState"},
|
||||||
|
{HasExtend, "hasExtend"},
|
||||||
|
{ExtendString, "extendString"}};
|
||||||
|
return roleNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::insertState(int stateIndex)
|
||||||
|
{
|
||||||
|
if (stateIndex >= 0) {
|
||||||
|
const int updateIndex = stateIndex + 1;
|
||||||
|
beginInsertRows(QModelIndex(), updateIndex, updateIndex);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
emit dataChanged(index(updateIndex, 0), index(updateIndex, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::updateState(int beginIndex, int endIndex)
|
||||||
|
{
|
||||||
|
if (beginIndex >= 0 && endIndex >= 0)
|
||||||
|
emit dataChanged(index(beginIndex, 0), index(endIndex, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::removeState(int stateIndex)
|
||||||
|
{
|
||||||
|
if (stateIndex >= 0) {
|
||||||
|
beginRemoveRows(QModelIndex(), 0, stateIndex);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::renameState(int internalNodeId, const QString &newName)
|
||||||
|
{
|
||||||
|
if (newName == m_statesEditorView->currentStateName())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (newName.isEmpty() ||! m_statesEditorView->validStateName(newName)) {
|
||||||
|
QTimer::singleShot(0, this, [newName] {
|
||||||
|
Core::AsynchronousMessageBox::warning(
|
||||||
|
tr("Invalid state name"),
|
||||||
|
newName.isEmpty() ?
|
||||||
|
tr("The empty string as a name is reserved for the base state.") :
|
||||||
|
tr("Name already used in another state"));
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
} else {
|
||||||
|
m_statesEditorView->renameState(internalNodeId, newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::setWhenCondition(int internalNodeId, const QString &condition)
|
||||||
|
{
|
||||||
|
m_statesEditorView->setWhenCondition(internalNodeId, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::resetWhenCondition(int internalNodeId)
|
||||||
|
{
|
||||||
|
m_statesEditorView->resetWhenCondition(internalNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList StatesEditorModel::autoComplete(const QString &text, int pos, bool explicitComplete)
|
||||||
|
{
|
||||||
|
Model *model = m_statesEditorView->model();
|
||||||
|
if (model && model->rewriterView())
|
||||||
|
return model->rewriterView()->autoComplete(text, pos, explicitComplete);
|
||||||
|
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StatesEditorModel::stateModelNode(int internalNodeId)
|
||||||
|
{
|
||||||
|
if (!m_statesEditorView->model())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
ModelNode node = m_statesEditorView->modelNodeForInternalId(internalNodeId);
|
||||||
|
|
||||||
|
return QVariant::fromValue(m_statesEditorView->modelNodeForInternalId(internalNodeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::setStateAsDefault(int internalNodeId)
|
||||||
|
{
|
||||||
|
m_statesEditorView->setStateAsDefault(internalNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::resetDefaultState()
|
||||||
|
{
|
||||||
|
m_statesEditorView->resetDefaultState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorModel::hasDefaultState() const
|
||||||
|
{
|
||||||
|
return m_statesEditorView->hasDefaultState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::setAnnotation(int internalNodeId)
|
||||||
|
{
|
||||||
|
m_statesEditorView->setAnnotation(internalNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::removeAnnotation(int internalNodeId)
|
||||||
|
{
|
||||||
|
m_statesEditorView->removeAnnotation(internalNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorModel::hasAnnotation(int internalNodeId) const
|
||||||
|
{
|
||||||
|
return m_statesEditorView->hasAnnotation(internalNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList StatesEditorModel::stateGroups() const
|
||||||
|
{
|
||||||
|
auto stateGroups = Utils::transform(m_statesEditorView->allModelNodesOfType(
|
||||||
|
"QtQuick.StateGroup"),
|
||||||
|
[](const ModelNode &node) { return node.displayName(); });
|
||||||
|
stateGroups.prepend(tr("Root"));
|
||||||
|
return stateGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StatesEditorModel::activeStateGroup() const
|
||||||
|
{
|
||||||
|
auto stateGroup = m_statesEditorView->activeStatesGroupNode();
|
||||||
|
|
||||||
|
if (!stateGroup.isValid())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return stateGroup.displayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::setActiveStateGroup(const QString &name)
|
||||||
|
{
|
||||||
|
auto modelNode = Utils::findOrDefault(m_statesEditorView->allModelNodesOfType(
|
||||||
|
"QtQuick.StateGroup"),
|
||||||
|
[&name](const ModelNode &node) {
|
||||||
|
return node.displayName() == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
QTC_ASSERT(!modelNode.isValid(), return );
|
||||||
|
|
||||||
|
if (modelNode.isValid())
|
||||||
|
m_statesEditorView->setActiveStatesGroupNode(modelNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StatesEditorModel::activeStateGroupIndex() const
|
||||||
|
{
|
||||||
|
return m_statesEditorView->activeStatesGroupIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::setActiveStateGroupIndex(int index)
|
||||||
|
{
|
||||||
|
m_statesEditorView->setActiveStatesGroupIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorModel::renameActiveStateGroup(const QString &name)
|
||||||
|
{
|
||||||
|
auto stateGroup = m_statesEditorView->activeStatesGroupNode();
|
||||||
|
|
||||||
|
if (!stateGroup.isValid() || stateGroup.isRootNode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!QmlDesigner::ModelNode::isValidId(name) || m_statesEditorView->hasId(name)) {
|
||||||
|
QString errMsg = QmlDesigner::ModelNode::getIdValidityErrorMessage(name);
|
||||||
|
if (!errMsg.isEmpty())
|
||||||
|
Core::AsynchronousMessageBox::warning(tr("Invalid ID"), errMsg);
|
||||||
|
else
|
||||||
|
Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
|
||||||
|
tr("%1 already exists.").arg(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateGroup.setIdWithRefactoring(name);
|
||||||
|
emit stateGroupsChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::addStateGroup(const QString &name)
|
||||||
|
{
|
||||||
|
m_statesEditorView->executeInTransaction("createStateGroup", [this, name]() {
|
||||||
|
const TypeName typeName = "QtQuick.StateGroup";
|
||||||
|
auto metaInfo = m_statesEditorView->model()->metaInfo(typeName);
|
||||||
|
int minorVersion = metaInfo.minorVersion();
|
||||||
|
int majorVersion = metaInfo.majorVersion();
|
||||||
|
auto stateGroupNode = m_statesEditorView->createModelNode(typeName,
|
||||||
|
majorVersion,
|
||||||
|
minorVersion);
|
||||||
|
stateGroupNode.setIdWithoutRefactoring(m_statesEditorView->model()->generateNewId(name));
|
||||||
|
|
||||||
|
m_statesEditorView->rootModelNode().defaultNodeAbstractProperty().reparentHere(
|
||||||
|
stateGroupNode);
|
||||||
|
m_statesEditorView->setActiveStatesGroupNode(stateGroupNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::removeStateGroup()
|
||||||
|
{
|
||||||
|
if (m_statesEditorView->activeStatesGroupNode().isRootNode())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_statesEditorView->executeInTransaction("removeStateGroup", [this]() {
|
||||||
|
m_statesEditorView->activeStatesGroupNode().destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap StatesEditorModel::get(int idx) const
|
||||||
|
{
|
||||||
|
const QHash<int, QByteArray> &names = roleNames();
|
||||||
|
QHash<int, QByteArray>::const_iterator i = names.constBegin();
|
||||||
|
|
||||||
|
QVariantMap res;
|
||||||
|
QModelIndex modelIndex = index(idx);
|
||||||
|
|
||||||
|
while (i != names.constEnd()) {
|
||||||
|
QVariant data = modelIndex.data(i.key());
|
||||||
|
|
||||||
|
res[QString::fromUtf8(i.value())] = data;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap StatesEditorModel::baseState() const
|
||||||
|
{
|
||||||
|
return get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorModel::hasExtend() const
|
||||||
|
{
|
||||||
|
return m_hasExtend;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList StatesEditorModel::extendedStates() const
|
||||||
|
{
|
||||||
|
return m_extendedStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::move(int from, int to)
|
||||||
|
{
|
||||||
|
// This does not alter the code (rewriter) which means the reordering is not presistent
|
||||||
|
|
||||||
|
if (from == to)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int specialIndex = (from < to ? to + 1 : to);
|
||||||
|
beginMoveRows(QModelIndex(), from, from, QModelIndex(), specialIndex);
|
||||||
|
endMoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::drop(int from, int to)
|
||||||
|
{
|
||||||
|
m_statesEditorView->moveStates(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorModel::evaluateExtend()
|
||||||
|
{
|
||||||
|
bool hasExtend = m_statesEditorView->hasExtend();
|
||||||
|
|
||||||
|
if (m_hasExtend != hasExtend) {
|
||||||
|
m_hasExtend = hasExtend;
|
||||||
|
emit hasExtendChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto extendedStates = m_statesEditorView->extendedStates();
|
||||||
|
|
||||||
|
if (extendedStates.size() != m_extendedStates.size()) {
|
||||||
|
m_extendedStates = extendedStates;
|
||||||
|
emit extendedStatesChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i != m_extendedStates.size(); ++i) {
|
||||||
|
if (extendedStates[i] != m_extendedStates[i]) {
|
||||||
|
m_extendedStates = extendedStates;
|
||||||
|
emit extendedStatesChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,127 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
class StatesEditorView;
|
||||||
|
|
||||||
|
class StatesEditorModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
enum {
|
||||||
|
StateNameRole = Qt::DisplayRole,
|
||||||
|
StateImageSourceRole = Qt::UserRole,
|
||||||
|
InternalNodeId,
|
||||||
|
HasWhenCondition,
|
||||||
|
WhenConditionString,
|
||||||
|
IsDefault,
|
||||||
|
ModelHasDefaultState,
|
||||||
|
HasExtend,
|
||||||
|
ExtendString
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
StatesEditorModel(StatesEditorView *view);
|
||||||
|
|
||||||
|
Q_INVOKABLE int count() const;
|
||||||
|
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
void insertState(int stateIndex);
|
||||||
|
void removeState(int stateIndex);
|
||||||
|
void updateState(int beginIndex, int endIndex);
|
||||||
|
Q_INVOKABLE void renameState(int internalNodeId, const QString &newName);
|
||||||
|
Q_INVOKABLE void setWhenCondition(int internalNodeId, const QString &condition);
|
||||||
|
Q_INVOKABLE void resetWhenCondition(int internalNodeId);
|
||||||
|
Q_INVOKABLE QStringList autoComplete(const QString &text, int pos, bool explicitComplete);
|
||||||
|
Q_INVOKABLE QVariant stateModelNode(int internalNodeId);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setStateAsDefault(int internalNodeId);
|
||||||
|
Q_INVOKABLE void resetDefaultState();
|
||||||
|
Q_INVOKABLE bool hasDefaultState() const;
|
||||||
|
Q_INVOKABLE void setAnnotation(int internalNodeId);
|
||||||
|
Q_INVOKABLE void removeAnnotation(int internalNodeId);
|
||||||
|
Q_INVOKABLE bool hasAnnotation(int internalNodeId) const;
|
||||||
|
|
||||||
|
QStringList stateGroups() const;
|
||||||
|
QString activeStateGroup() const;
|
||||||
|
void setActiveStateGroup(const QString &name);
|
||||||
|
int activeStateGroupIndex() const;
|
||||||
|
void setActiveStateGroupIndex(int index);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool renameActiveStateGroup(const QString &name);
|
||||||
|
|
||||||
|
Q_INVOKABLE void addStateGroup(const QString &name);
|
||||||
|
Q_INVOKABLE void removeStateGroup();
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariantMap get(int idx) const;
|
||||||
|
|
||||||
|
QVariantMap baseState() const;
|
||||||
|
bool hasExtend() const;
|
||||||
|
QStringList extendedStates() const;
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariantMap baseState READ baseState NOTIFY baseStateChanged)
|
||||||
|
Q_PROPERTY(QStringList extendedStates READ extendedStates NOTIFY extendedStatesChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool hasExtend READ hasExtend NOTIFY hasExtendChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString activeStateGroup READ activeStateGroup WRITE setActiveStateGroup NOTIFY
|
||||||
|
activeStateGroupChanged)
|
||||||
|
Q_PROPERTY(int activeStateGroupIndex READ activeStateGroupIndex WRITE setActiveStateGroupIndex
|
||||||
|
NOTIFY activeStateGroupIndexChanged)
|
||||||
|
Q_PROPERTY(QStringList stateGroups READ stateGroups NOTIFY stateGroupsChanged)
|
||||||
|
|
||||||
|
Q_INVOKABLE void move(int from, int to);
|
||||||
|
Q_INVOKABLE void drop(int from, int to);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void evaluateExtend();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changedToState(int n);
|
||||||
|
void baseStateChanged();
|
||||||
|
void hasExtendChanged();
|
||||||
|
void extendedStatesChanged();
|
||||||
|
void activeStateGroupChanged();
|
||||||
|
void activeStateGroupIndexChanged();
|
||||||
|
void stateGroupsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<StatesEditorView> m_statesEditorView;
|
||||||
|
bool m_hasExtend;
|
||||||
|
QStringList m_extendedStates;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,936 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "stateseditorview.h"
|
||||||
|
#include "propertychangesmodel.h"
|
||||||
|
#include "stateseditormodel.h"
|
||||||
|
#include "stateseditorwidget.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <nodemetainfo.h>
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <customnotifications.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <rewritingexception.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
#include <qmlitemnode.h>
|
||||||
|
#include <qmlstate.h>
|
||||||
|
#include <annotationeditor/annotationeditor.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
/**
|
||||||
|
We always have 'one' current state, where we get updates from (see sceneChanged()). In case
|
||||||
|
the current state is the base state, we render the base state + all other states.
|
||||||
|
*/
|
||||||
|
StatesEditorView::StatesEditorView(QObject *parent) :
|
||||||
|
AbstractView(parent),
|
||||||
|
m_statesEditorModel(new StatesEditorModel(this)),
|
||||||
|
m_lastIndex(-1),
|
||||||
|
m_editor(nullptr)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_statesEditorModel);
|
||||||
|
// base state
|
||||||
|
}
|
||||||
|
|
||||||
|
StatesEditorView::~StatesEditorView()
|
||||||
|
{
|
||||||
|
if (m_editor)
|
||||||
|
delete m_editor;
|
||||||
|
delete m_statesEditorWidget.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetInfo StatesEditorView::widgetInfo()
|
||||||
|
{
|
||||||
|
if (!m_statesEditorWidget)
|
||||||
|
m_statesEditorWidget = new StatesEditorWidget(this, m_statesEditorModel.data());
|
||||||
|
|
||||||
|
return createWidgetInfo(m_statesEditorWidget.data(),
|
||||||
|
QLatin1String("StatesEditorNew"),
|
||||||
|
WidgetInfo::BottomPane,
|
||||||
|
0,
|
||||||
|
tr("States New"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
|
||||||
|
{
|
||||||
|
checkForStatesAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelNode StatesEditorView::activeStatesGroupNode() const
|
||||||
|
{
|
||||||
|
return m_activeStatesGroupNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setActiveStatesGroupNode(const ModelNode &modelNode)
|
||||||
|
{
|
||||||
|
if (m_activeStatesGroupNode == modelNode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_activeStatesGroupNode = modelNode;
|
||||||
|
resetModel();
|
||||||
|
|
||||||
|
emit m_statesEditorModel->activeStateGroupChanged();
|
||||||
|
emit m_statesEditorModel->activeStateGroupIndexChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int StatesEditorView::activeStatesGroupIndex() const
|
||||||
|
{
|
||||||
|
return Utils::indexOf(allModelNodesOfType("QtQuick.StateGroup"),
|
||||||
|
[this](const ModelNode &node) { return node == m_activeStatesGroupNode; })
|
||||||
|
+ 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setActiveStatesGroupIndex(int index)
|
||||||
|
{
|
||||||
|
if (index > 0) {
|
||||||
|
const ModelNode statesGroup = allModelNodesOfType("QtQuick.StateGroup").at(index - 1);
|
||||||
|
if (statesGroup.isValid())
|
||||||
|
setActiveStatesGroupNode(statesGroup);
|
||||||
|
} else {
|
||||||
|
setActiveStatesGroupNode(rootModelNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::registerPropertyChangesModel(PropertyChangesModel *model)
|
||||||
|
{
|
||||||
|
m_propertyChangedModels.insert(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::deregisterPropertyChangesModel(PropertyChangesModel *model)
|
||||||
|
{
|
||||||
|
m_propertyChangedModels.remove(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::synchonizeCurrentStateFromWidget()
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int internalId = m_statesEditorWidget->currentStateInternalId();
|
||||||
|
|
||||||
|
if (internalId > 0 && hasModelNodeForInternalId(internalId)) {
|
||||||
|
ModelNode node = modelNodeForInternalId(internalId);
|
||||||
|
QmlModelState modelState(node);
|
||||||
|
if (modelState.isValid() && modelState != currentState())
|
||||||
|
setCurrentState(modelState);
|
||||||
|
} else {
|
||||||
|
setCurrentState(baseState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::createNewState()
|
||||||
|
{
|
||||||
|
// can happen when root node is e.g. a ListModel
|
||||||
|
if (!QmlVisualNode::isValidQmlVisualNode(activeStatesGroupNode())
|
||||||
|
&& m_activeStatesGroupNode.type() != "QtQuick.StateGroup")
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATE_ADDED);
|
||||||
|
|
||||||
|
QStringList modelStateNames = activeStateGroup().names();
|
||||||
|
|
||||||
|
QString newStateName;
|
||||||
|
int index = 1;
|
||||||
|
while (true) {
|
||||||
|
newStateName = QString(QStringLiteral("State%1")).arg(index++);
|
||||||
|
if (!modelStateNames.contains(newStateName))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
executeInTransaction("createNewState", [this, newStateName]() {
|
||||||
|
activeStatesGroupNode().validId();
|
||||||
|
|
||||||
|
ModelNode newState = activeStateGroup().addState(newStateName);
|
||||||
|
setCurrentState(newState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::cloneState(int nodeId)
|
||||||
|
{
|
||||||
|
if (!(nodeId > 0 && hasModelNodeForInternalId(nodeId)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ModelNode stateNode(modelNodeForInternalId(nodeId));
|
||||||
|
QTC_ASSERT(stateNode.simplifiedTypeName() == "State", return );
|
||||||
|
|
||||||
|
QmlModelState modelState(stateNode);
|
||||||
|
if (!modelState.isValid() || modelState.isBaseState())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATE_CLONED);
|
||||||
|
|
||||||
|
QString newName = modelState.name();
|
||||||
|
|
||||||
|
// Strip out numbers at the end of the string
|
||||||
|
QRegularExpression regEx(QLatin1String("[0-9]+$"));
|
||||||
|
const QRegularExpressionMatch match = regEx.match(newName);
|
||||||
|
if (match.hasMatch() && (match.capturedStart() + match.capturedLength() == newName.length()))
|
||||||
|
newName = newName.left(match.capturedStart());
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
QStringList stateNames = activeStateGroup().names();
|
||||||
|
while (stateNames.contains(newName + QString::number(i)))
|
||||||
|
i++;
|
||||||
|
const QString newStateName = newName + QString::number(i);
|
||||||
|
|
||||||
|
QmlModelState newState;
|
||||||
|
|
||||||
|
executeInTransaction("cloneState", [newStateName, modelState, &newState]() {
|
||||||
|
newState = modelState.duplicate(newStateName);
|
||||||
|
});
|
||||||
|
|
||||||
|
ModelNode newNode = newState.modelNode();
|
||||||
|
int from = newNode.parentProperty().indexOf(newNode);
|
||||||
|
int to = stateNode.parentProperty().indexOf(stateNode) + 1;
|
||||||
|
|
||||||
|
executeInTransaction("moveState", [this, &newState, from, to]() {
|
||||||
|
activeStatesGroupNode().nodeListProperty("states").slide(from, to);
|
||||||
|
setCurrentState(newState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::extendState(int nodeId)
|
||||||
|
{
|
||||||
|
if (!(nodeId > 0 && hasModelNodeForInternalId(nodeId)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ModelNode stateNode(modelNodeForInternalId(nodeId));
|
||||||
|
QTC_ASSERT(stateNode.simplifiedTypeName() == "State", return );
|
||||||
|
|
||||||
|
QmlModelState modelState(stateNode);
|
||||||
|
if (!modelState.isValid() || modelState.isBaseState())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATE_EXTENDED);
|
||||||
|
|
||||||
|
QString newName = modelState.name();
|
||||||
|
|
||||||
|
// Strip out numbers at the end of the string
|
||||||
|
QRegularExpression regEx(QLatin1String("[0-9]+$"));
|
||||||
|
const QRegularExpressionMatch match = regEx.match(newName);
|
||||||
|
if (match.hasMatch() && (match.capturedStart() + match.capturedLength() == newName.length()))
|
||||||
|
newName = newName.left(match.capturedStart());
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
QStringList stateNames = activeStateGroup().names();
|
||||||
|
while (stateNames.contains(newName + QString::number(i)))
|
||||||
|
i++;
|
||||||
|
const QString newStateName = newName + QString::number(i);
|
||||||
|
|
||||||
|
QmlModelState newState;
|
||||||
|
|
||||||
|
executeInTransaction("extendState", [this, newStateName, modelState, &newState]() {
|
||||||
|
newState = activeStateGroup().addState(newStateName);
|
||||||
|
newState.setExtend(modelState.name());
|
||||||
|
});
|
||||||
|
|
||||||
|
ModelNode newNode = newState.modelNode();
|
||||||
|
int from = newNode.parentProperty().indexOf(newNode);
|
||||||
|
int to = stateNode.parentProperty().indexOf(stateNode) + 1;
|
||||||
|
|
||||||
|
executeInTransaction("moveState", [this, &newState, from, to]() {
|
||||||
|
activeStatesGroupNode().nodeListProperty("states").slide(from, to);
|
||||||
|
setCurrentState(newState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::removeState(int nodeId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (nodeId > 0 && hasModelNodeForInternalId(nodeId)) {
|
||||||
|
ModelNode stateNode(modelNodeForInternalId(nodeId));
|
||||||
|
QTC_ASSERT(stateNode.simplifiedTypeName() == "State", return );
|
||||||
|
|
||||||
|
QmlModelState modelState(stateNode);
|
||||||
|
if (modelState.isValid()) {
|
||||||
|
QStringList lockedTargets;
|
||||||
|
const auto propertyChanges = modelState.propertyChanges();
|
||||||
|
|
||||||
|
// confirm removing not empty states
|
||||||
|
if (!propertyChanges.isEmpty()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
|
msgBox.setIcon(QMessageBox::Question);
|
||||||
|
msgBox.setWindowTitle(tr("Remove State"));
|
||||||
|
msgBox.setText(
|
||||||
|
tr("This state is not empty. Are you sure you want to remove it?"));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||||
|
|
||||||
|
if (msgBox.exec() == QMessageBox::Cancel)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm removing states with locked targets
|
||||||
|
for (const QmlPropertyChanges &change : propertyChanges) {
|
||||||
|
const ModelNode target = change.target();
|
||||||
|
QTC_ASSERT(target.isValid(), continue);
|
||||||
|
if (target.locked())
|
||||||
|
lockedTargets.push_back(target.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lockedTargets.empty()) {
|
||||||
|
Utils::sort(lockedTargets);
|
||||||
|
QString detailedText = QString("<b>" + tr("Locked components:") + "</b><br>");
|
||||||
|
|
||||||
|
for (const auto &id : qAsConst(lockedTargets))
|
||||||
|
detailedText.append("- " + id + "<br>");
|
||||||
|
|
||||||
|
detailedText.chop(QString("<br>").size());
|
||||||
|
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setTextFormat(Qt::RichText);
|
||||||
|
msgBox.setIcon(QMessageBox::Question);
|
||||||
|
msgBox.setWindowTitle(tr("Remove State"));
|
||||||
|
msgBox.setText(QString(tr("Removing this state will modify locked components.")
|
||||||
|
+ "<br><br>%1")
|
||||||
|
.arg(detailedText));
|
||||||
|
msgBox.setInformativeText(tr("Continue by removing the state?"));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||||
|
|
||||||
|
if (msgBox.exec() == QMessageBox::Cancel)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeListProperty parentProperty = stateNode.parentProperty().toNodeListProperty();
|
||||||
|
|
||||||
|
if (parentProperty.count() <= 1) {
|
||||||
|
setCurrentState(baseState());
|
||||||
|
} else if (parentProperty.isValid()) {
|
||||||
|
int index = parentProperty.indexOf(stateNode);
|
||||||
|
if (index == 0)
|
||||||
|
setCurrentState(parentProperty.at(1));
|
||||||
|
else
|
||||||
|
setCurrentState(parentProperty.at(index - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
stateNode.destroy();
|
||||||
|
}
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetModel()
|
||||||
|
{
|
||||||
|
if (m_bulkChange) {
|
||||||
|
m_modelDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_statesEditorModel)
|
||||||
|
m_statesEditorModel->reset();
|
||||||
|
|
||||||
|
if (m_statesEditorWidget) {
|
||||||
|
if (currentState().isBaseState())
|
||||||
|
m_statesEditorWidget->setCurrentStateInternalId(0);
|
||||||
|
else
|
||||||
|
m_statesEditorWidget->setCurrentStateInternalId(currentState().modelNode().internalId());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modelDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetPropertyChangesModels()
|
||||||
|
{
|
||||||
|
if (m_bulkChange) {
|
||||||
|
m_propertyChangesDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::for_each(m_propertyChangedModels.begin(),
|
||||||
|
m_propertyChangedModels.end(),
|
||||||
|
[](PropertyChangesModel *model) { model->reset(); });
|
||||||
|
|
||||||
|
m_propertyChangesDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetExtend()
|
||||||
|
{
|
||||||
|
if (m_bulkChange) {
|
||||||
|
m_extendDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_statesEditorModel->evaluateExtend();
|
||||||
|
|
||||||
|
m_extendDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetStateGroups()
|
||||||
|
{
|
||||||
|
if (m_bulkChange) {
|
||||||
|
m_stateGroupsDirty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit m_statesEditorModel->stateGroupsChanged();
|
||||||
|
|
||||||
|
m_stateGroupsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::checkForStatesAvailability()
|
||||||
|
{
|
||||||
|
if (m_statesEditorWidget) {
|
||||||
|
const bool isVisual = QmlVisualNode::isValidQmlVisualNode(activeStatesGroupNode());
|
||||||
|
m_statesEditorWidget->showAddNewStatesButton(isVisual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::beginBulkChange()
|
||||||
|
{
|
||||||
|
m_bulkChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::endBulkChange()
|
||||||
|
{
|
||||||
|
if (!m_bulkChange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_bulkChange = false;
|
||||||
|
|
||||||
|
if (m_modelDirty)
|
||||||
|
resetModel();
|
||||||
|
|
||||||
|
if (m_propertyChangesDirty)
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
|
||||||
|
if (m_extendDirty)
|
||||||
|
resetExtend();
|
||||||
|
|
||||||
|
if (m_stateGroupsDirty)
|
||||||
|
resetStateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setCurrentState(const QmlModelState &state)
|
||||||
|
{
|
||||||
|
if (!model() && !state.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (currentStateNode() != state.modelNode())
|
||||||
|
setCurrentStateNode(state.modelNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlModelState StatesEditorView::baseState() const
|
||||||
|
{
|
||||||
|
return QmlModelState::createBaseState(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlModelStateGroup StatesEditorView::activeStateGroup() const
|
||||||
|
{
|
||||||
|
return QmlModelStateGroup(activeStatesGroupNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorView::validStateName(const QString &name) const
|
||||||
|
{
|
||||||
|
if (name == tr("base state"))
|
||||||
|
return false;
|
||||||
|
const QList<QmlModelState> modelStates = activeStateGroup().allStates();
|
||||||
|
for (const QmlModelState &state : modelStates) {
|
||||||
|
if (state.name() == name)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorView::hasExtend() const
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QList<QmlModelState> modelStates = activeStateGroup().allStates();
|
||||||
|
for (const QmlModelState &state : modelStates) {
|
||||||
|
if (state.hasExtend())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList StatesEditorView::extendedStates() const
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return QStringList();
|
||||||
|
|
||||||
|
QStringList states;
|
||||||
|
|
||||||
|
const QList<QmlModelState> modelStates = activeStateGroup().allStates();
|
||||||
|
for (const QmlModelState &state : modelStates) {
|
||||||
|
if (state.hasExtend())
|
||||||
|
states.append(state.extend());
|
||||||
|
}
|
||||||
|
states.removeDuplicates();
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StatesEditorView::currentStateName() const
|
||||||
|
{
|
||||||
|
return currentState().isValid() ? currentState().name() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::renameState(int internalNodeId, const QString &newName)
|
||||||
|
{
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState renamedState(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (renamedState.isValid() && renamedState.name() != newName) {
|
||||||
|
executeInTransaction("renameState", [this, &renamedState, &newName]() {
|
||||||
|
// Jump to base state for the change
|
||||||
|
QmlModelState oldState = currentState();
|
||||||
|
setCurrentState(baseState());
|
||||||
|
const bool updateDefault = renamedState.isDefault();
|
||||||
|
|
||||||
|
// If state is extended rename all references
|
||||||
|
QList<QmlModelState> states;
|
||||||
|
const QList<QmlModelState> modelStates = activeStateGroup().allStates();
|
||||||
|
for (const QmlModelState &state : modelStates) {
|
||||||
|
if (state.hasExtend() && state.extend() == renamedState.name())
|
||||||
|
states.append(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
renamedState.setName(newName.trimmed());
|
||||||
|
|
||||||
|
for (QmlModelState &state : states)
|
||||||
|
state.setExtend(newName.trimmed());
|
||||||
|
|
||||||
|
if (updateDefault)
|
||||||
|
renamedState.setAsDefault();
|
||||||
|
|
||||||
|
setCurrentState(oldState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setWhenCondition(int internalNodeId, const QString &condition)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (state.isValid())
|
||||||
|
state.modelNode().bindingProperty("when").setExpression(condition);
|
||||||
|
|
||||||
|
} catch (const Exception &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetWhenCondition(int internalNodeId)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (state.isValid() && state.modelNode().hasProperty("when"))
|
||||||
|
state.modelNode().removeProperty("when");
|
||||||
|
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setStateAsDefault(int internalNodeId)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (state.isValid())
|
||||||
|
state.setAsDefault();
|
||||||
|
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::resetDefaultState()
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (activeStatesGroupNode().hasProperty("state"))
|
||||||
|
activeStatesGroupNode().removeProperty("state");
|
||||||
|
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorView::hasDefaultState() const
|
||||||
|
{
|
||||||
|
return activeStatesGroupNode().hasProperty("state");
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::setAnnotation(int internalNodeId)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (state.isValid()) {
|
||||||
|
ModelNode modelNode = state.modelNode();
|
||||||
|
|
||||||
|
if (modelNode.isValid()) {
|
||||||
|
if (!m_editor)
|
||||||
|
m_editor = new AnnotationEditor(this);
|
||||||
|
|
||||||
|
m_editor->setModelNode(modelNode);
|
||||||
|
m_editor->showWidget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::removeAnnotation(int internalNodeId)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
try {
|
||||||
|
if (state.isValid())
|
||||||
|
state.removeAnnotation();
|
||||||
|
|
||||||
|
} catch (const RewritingException &e) {
|
||||||
|
e.showException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StatesEditorView::hasAnnotation(int internalNodeId) const
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hasModelNodeForInternalId(internalNodeId)) {
|
||||||
|
QmlModelState state(modelNodeForInternalId(internalNodeId));
|
||||||
|
if (state.isValid())
|
||||||
|
return state.hasAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::modelAttached(Model *model)
|
||||||
|
{
|
||||||
|
if (model == AbstractView::model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTC_ASSERT(model, return );
|
||||||
|
AbstractView::modelAttached(model);
|
||||||
|
|
||||||
|
m_activeStatesGroupNode = rootModelNode();
|
||||||
|
|
||||||
|
if (m_statesEditorWidget)
|
||||||
|
m_statesEditorWidget->setNodeInstanceView(nodeInstanceView());
|
||||||
|
|
||||||
|
checkForStatesAvailability();
|
||||||
|
|
||||||
|
resetModel();
|
||||||
|
resetStateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::modelAboutToBeDetached(Model *model)
|
||||||
|
{
|
||||||
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::propertiesRemoved(const QList<AbstractProperty>& propertyList)
|
||||||
|
{
|
||||||
|
for (const AbstractProperty &property : propertyList) {
|
||||||
|
if (property.name() == "states" && property.parentModelNode() == activeStateGroup().modelNode())
|
||||||
|
resetModel();
|
||||||
|
if (property.name() == "when"
|
||||||
|
&& QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||||
|
resetModel();
|
||||||
|
if (property.name() == "extend")
|
||||||
|
resetExtend();
|
||||||
|
if (property.parentModelNode().simplifiedTypeName() == "PropertyChanges"
|
||||||
|
|| (property.name() == "changes"
|
||||||
|
&& property.parentModelNode().simplifiedTypeName() == "State"))
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
||||||
|
{
|
||||||
|
if (removedNode.hasParentProperty()) {
|
||||||
|
const NodeAbstractProperty propertyParent = removedNode.parentProperty();
|
||||||
|
if (propertyParent.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& propertyParent.name() == "states")
|
||||||
|
m_lastIndex = propertyParent.indexOf(removedNode);
|
||||||
|
}
|
||||||
|
if (currentState().isValid() && removedNode == currentState())
|
||||||
|
setCurrentState(baseState());
|
||||||
|
|
||||||
|
if (removedNode.simplifiedTypeName() == "PropertyChanges")
|
||||||
|
m_propertyChangesRemoved = true;
|
||||||
|
|
||||||
|
if (removedNode.simplifiedTypeName() == "StateGroup") {
|
||||||
|
if (removedNode == activeStatesGroupNode())
|
||||||
|
setActiveStatesGroupNode(rootModelNode());
|
||||||
|
|
||||||
|
m_stateGroupRemoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::nodeRemoved(const ModelNode & /*removedNode*/,
|
||||||
|
const NodeAbstractProperty &parentProperty,
|
||||||
|
PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (parentProperty.isValid()
|
||||||
|
&& parentProperty.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& parentProperty.name() == "states") {
|
||||||
|
m_statesEditorModel->removeState(m_lastIndex);
|
||||||
|
m_lastIndex = -1;
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_propertyChangesRemoved) {
|
||||||
|
m_propertyChangesRemoved = false;
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_stateGroupRemoved) {
|
||||||
|
m_stateGroupRemoved = false;
|
||||||
|
resetStateGroups();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::nodeAboutToBeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty & /*newPropertyParent*/,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (oldPropertyParent.isValid()
|
||||||
|
&& oldPropertyParent.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& oldPropertyParent.name() == "states")
|
||||||
|
m_lastIndex = oldPropertyParent.indexOf(node);
|
||||||
|
|
||||||
|
if (node.simplifiedTypeName() == "StateGroup")
|
||||||
|
resetStateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::nodeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (oldPropertyParent.isValid()
|
||||||
|
&& oldPropertyParent.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& oldPropertyParent.name() == "states") {
|
||||||
|
m_statesEditorModel->removeState(m_lastIndex);
|
||||||
|
resetModel();
|
||||||
|
m_lastIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPropertyParent.isValid()
|
||||||
|
&& newPropertyParent.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& newPropertyParent.name() == "states") {
|
||||||
|
int index = newPropertyParent.indexOf(node);
|
||||||
|
m_statesEditorModel->insertState(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.simplifiedTypeName() == "PropertyChanges")
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::nodeOrderChanged(const NodeListProperty &listProperty)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (listProperty.isValid() && listProperty.parentModelNode() == activeStateGroup().modelNode()
|
||||||
|
&& listProperty.name() == "states")
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, AbstractView::PropertyChangeFlags propertyChange)
|
||||||
|
{
|
||||||
|
Q_UNUSED(propertyChange)
|
||||||
|
|
||||||
|
for (const BindingProperty &property : propertyList) {
|
||||||
|
if (property.name() == "when"
|
||||||
|
&& QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||||
|
resetModel();
|
||||||
|
if (property.parentModelNode().simplifiedTypeName() == "PropertyChanges")
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
||||||
|
AbstractView::PropertyChangeFlags /*propertyChange*/)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
for (const VariantProperty &property : propertyList) {
|
||||||
|
if (property.name() == "name" && QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||||
|
resetModel();
|
||||||
|
else if (property.name() == "state"
|
||||||
|
&& property.parentModelNode() == activeStateGroup().modelNode())
|
||||||
|
resetModel();
|
||||||
|
else if (property.name() == "extend")
|
||||||
|
resetExtend();
|
||||||
|
|
||||||
|
if (property.parentModelNode().simplifiedTypeName() == "PropertyChanges")
|
||||||
|
resetPropertyChangesModels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::customNotification(const AbstractView * /*view*/,
|
||||||
|
const QString &identifier,
|
||||||
|
const QList<ModelNode> & /*nodeList*/,
|
||||||
|
const QList<QVariant> & /*data*/)
|
||||||
|
{
|
||||||
|
if (identifier == StartRewriterAmend)
|
||||||
|
beginBulkChange();
|
||||||
|
|
||||||
|
if (identifier == EndRewriterAmend)
|
||||||
|
endBulkChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::rewriterBeginTransaction()
|
||||||
|
{
|
||||||
|
beginBulkChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::rewriterEndTransaction()
|
||||||
|
{
|
||||||
|
endBulkChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::currentStateChanged(const ModelNode &node)
|
||||||
|
{
|
||||||
|
QmlModelState newQmlModelState(node);
|
||||||
|
|
||||||
|
if (newQmlModelState.isBaseState())
|
||||||
|
m_statesEditorWidget->setCurrentStateInternalId(0);
|
||||||
|
else
|
||||||
|
m_statesEditorWidget->setCurrentStateInternalId(newQmlModelState.modelNode().internalId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::instancesPreviewImageChanged(const QVector<ModelNode> &nodeList)
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int minimumIndex = 10000;
|
||||||
|
int maximumIndex = -1;
|
||||||
|
for (const ModelNode &node : nodeList) {
|
||||||
|
if (node.isRootNode()) {
|
||||||
|
minimumIndex = qMin(minimumIndex, 0);
|
||||||
|
maximumIndex = qMax(maximumIndex, 0);
|
||||||
|
} else {
|
||||||
|
int index = activeStateGroup().allStates().indexOf(QmlModelState(node)) + 1;
|
||||||
|
if (index > 0) {
|
||||||
|
minimumIndex = qMin(minimumIndex, index);
|
||||||
|
maximumIndex = qMax(maximumIndex, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maximumIndex >= 0)
|
||||||
|
m_statesEditorModel->updateState(minimumIndex, maximumIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorView::moveStates(int from, int to)
|
||||||
|
{
|
||||||
|
if (m_block)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_block = true;
|
||||||
|
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||||
|
|
||||||
|
if (!activeStatesGroupNode().hasNodeListProperty("states"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
executeInTransaction("moveState", [this, from, to]() {
|
||||||
|
activeStatesGroupNode().nodeListProperty("states").slide(from - 1, to - 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,158 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <abstractview.h>
|
||||||
|
|
||||||
|
#include <qmlstate.h>
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class AnnotationEditor;
|
||||||
|
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
class StatesEditorModel;
|
||||||
|
class StatesEditorWidget;
|
||||||
|
class PropertyChangesModel;
|
||||||
|
|
||||||
|
class StatesEditorView : public AbstractView {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit StatesEditorView(QObject *parent = nullptr);
|
||||||
|
~StatesEditorView() override;
|
||||||
|
|
||||||
|
void renameState(int internalNodeId,const QString &newName);
|
||||||
|
void setWhenCondition(int internalNodeId, const QString &condition);
|
||||||
|
void resetWhenCondition(int internalNodeId);
|
||||||
|
void setStateAsDefault(int internalNodeId);
|
||||||
|
void resetDefaultState();
|
||||||
|
bool hasDefaultState() const;
|
||||||
|
void setAnnotation(int internalNodeId);
|
||||||
|
void removeAnnotation(int internalNodeId);
|
||||||
|
bool hasAnnotation(int internalNodeId) const;
|
||||||
|
bool validStateName(const QString &name) const;
|
||||||
|
bool hasExtend() const;
|
||||||
|
QStringList extendedStates() const;
|
||||||
|
QString currentStateName() const;
|
||||||
|
void setCurrentState(const QmlModelState &state);
|
||||||
|
QmlModelState baseState() const;
|
||||||
|
QmlModelStateGroup activeStateGroup() const;
|
||||||
|
|
||||||
|
void moveStates(int from, int to);
|
||||||
|
|
||||||
|
// AbstractView
|
||||||
|
void modelAttached(Model *model) override;
|
||||||
|
void modelAboutToBeDetached(Model *model) override;
|
||||||
|
void propertiesRemoved(const QList<AbstractProperty>& propertyList) override;
|
||||||
|
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
|
||||||
|
void nodeRemoved(const ModelNode &removedNode,
|
||||||
|
const NodeAbstractProperty &parentProperty,
|
||||||
|
PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeAboutToBeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeReparented(const ModelNode &node,
|
||||||
|
const NodeAbstractProperty &newPropertyParent,
|
||||||
|
const NodeAbstractProperty &oldPropertyParent,
|
||||||
|
AbstractView::PropertyChangeFlags propertyChange) override;
|
||||||
|
void nodeOrderChanged(const NodeListProperty &listProperty) override;
|
||||||
|
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
|
||||||
|
PropertyChangeFlags propertyChange) override;
|
||||||
|
void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
||||||
|
PropertyChangeFlags propertyChange) override;
|
||||||
|
|
||||||
|
void customNotification(const AbstractView *view,
|
||||||
|
const QString &identifier,
|
||||||
|
const QList<ModelNode> &nodeList,
|
||||||
|
const QList<QVariant> &data) override;
|
||||||
|
void rewriterBeginTransaction() override;
|
||||||
|
void rewriterEndTransaction() override;
|
||||||
|
|
||||||
|
// AbstractView
|
||||||
|
void currentStateChanged(const ModelNode &node) override;
|
||||||
|
|
||||||
|
void instancesPreviewImageChanged(const QVector<ModelNode> &nodeList) override;
|
||||||
|
|
||||||
|
WidgetInfo widgetInfo() override;
|
||||||
|
|
||||||
|
void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override;
|
||||||
|
|
||||||
|
ModelNode activeStatesGroupNode() const;
|
||||||
|
void setActiveStatesGroupNode(const ModelNode &modelNode);
|
||||||
|
|
||||||
|
int activeStatesGroupIndex() const;
|
||||||
|
void setActiveStatesGroupIndex(int index);
|
||||||
|
|
||||||
|
void registerPropertyChangesModel(PropertyChangesModel *model);
|
||||||
|
void deregisterPropertyChangesModel(PropertyChangesModel *model);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void synchonizeCurrentStateFromWidget();
|
||||||
|
void createNewState();
|
||||||
|
void cloneState(int nodeId);
|
||||||
|
void extendState(int nodeId);
|
||||||
|
void removeState(int nodeId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatesEditorWidget *statesEditorWidget() const;
|
||||||
|
void resetModel();
|
||||||
|
void resetPropertyChangesModels();
|
||||||
|
void resetExtend();
|
||||||
|
void resetStateGroups();
|
||||||
|
|
||||||
|
void checkForStatesAvailability();
|
||||||
|
|
||||||
|
void beginBulkChange();
|
||||||
|
void endBulkChange();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<StatesEditorModel> m_statesEditorModel;
|
||||||
|
QPointer<StatesEditorWidget> m_statesEditorWidget;
|
||||||
|
int m_lastIndex;
|
||||||
|
bool m_block = false;
|
||||||
|
QPointer<AnnotationEditor> m_editor;
|
||||||
|
ModelNode m_activeStatesGroupNode;
|
||||||
|
|
||||||
|
bool m_propertyChangesRemoved = false;
|
||||||
|
bool m_stateGroupRemoved = false;
|
||||||
|
|
||||||
|
bool m_bulkChange = false;
|
||||||
|
|
||||||
|
bool m_modelDirty = false;
|
||||||
|
bool m_extendDirty = false;
|
||||||
|
bool m_propertyChangesDirty = false;
|
||||||
|
bool m_stateGroupsDirty = false;
|
||||||
|
|
||||||
|
QSet<PropertyChangesModel *> m_propertyChangedModels;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,199 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "stateseditorwidget.h"
|
||||||
|
#include "stateseditormodel.h"
|
||||||
|
#include "stateseditorview.h"
|
||||||
|
#include "stateseditorimageprovider.h"
|
||||||
|
|
||||||
|
#include <designersettings.h>
|
||||||
|
#include <theme.h>
|
||||||
|
#include <qmldesignerconstants.h>
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
|
#include <invalidqmlsourceexception.h>
|
||||||
|
|
||||||
|
#include <coreplugin/messagebox.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/stylehelper.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QKeySequence>
|
||||||
|
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
debug = false
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
int StatesEditorWidget::currentStateInternalId() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(rootObject(), return -1);
|
||||||
|
QTC_ASSERT(rootObject()->property("currentStateInternalId").isValid(), return -1);
|
||||||
|
|
||||||
|
return rootObject()->property("currentStateInternalId").toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::setCurrentStateInternalId(int internalId)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(rootObject(), return);
|
||||||
|
rootObject()->setProperty("currentStateInternalId", internalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::setNodeInstanceView(NodeInstanceView *nodeInstanceView)
|
||||||
|
{
|
||||||
|
m_imageProvider->setNodeInstanceView(nodeInstanceView);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::showAddNewStatesButton(bool showAddNewStatesButton)
|
||||||
|
{
|
||||||
|
rootContext()->setContextProperty(QLatin1String("canAddNewStates"), showAddNewStatesButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView,
|
||||||
|
StatesEditorModel *statesEditorModel)
|
||||||
|
: m_statesEditorView(statesEditorView)
|
||||||
|
, m_imageProvider(nullptr)
|
||||||
|
, m_qmlSourceUpdateShortcut(nullptr)
|
||||||
|
{
|
||||||
|
m_imageProvider = new Internal::StatesEditorImageProvider;
|
||||||
|
m_imageProvider->setNodeInstanceView(statesEditorView->nodeInstanceView());
|
||||||
|
|
||||||
|
engine()->addImageProvider(QStringLiteral("qmldesigner_stateseditor"), m_imageProvider);
|
||||||
|
engine()->addImportPath(qmlSourcesPath());
|
||||||
|
engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||||
|
engine()->addImportPath(qmlSourcesPath() + "/imports");
|
||||||
|
|
||||||
|
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F10), this);
|
||||||
|
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &StatesEditorWidget::reloadQmlSource);
|
||||||
|
|
||||||
|
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
rootContext()->setContextProperties(
|
||||||
|
QVector<QQmlContext::PropertyPair>{{{"statesEditorModel"},
|
||||||
|
QVariant::fromValue(statesEditorModel)},
|
||||||
|
{{"canAddNewStates"}, true}});
|
||||||
|
|
||||||
|
Theme::setupTheme(engine());
|
||||||
|
|
||||||
|
setWindowTitle(tr("States New", "Title of Editor widget"));
|
||||||
|
setMinimumWidth(195);
|
||||||
|
setMinimumHeight(195);
|
||||||
|
|
||||||
|
// init the first load of the QML UI elements
|
||||||
|
reloadQmlSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
StatesEditorWidget::~StatesEditorWidget() = default;
|
||||||
|
|
||||||
|
QString StatesEditorWidget::qmlSourcesPath()
|
||||||
|
{
|
||||||
|
#ifdef SHARE_QML_PATH
|
||||||
|
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||||
|
return QLatin1String(SHARE_QML_PATH) + "/newstateseditor";
|
||||||
|
#endif
|
||||||
|
return Core::ICore::resourcePath("qmldesigner/newstateseditor").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QQuickWidget::showEvent(event);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::focusOutEvent(QFocusEvent *focusEvent)
|
||||||
|
{
|
||||||
|
QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_STATESEDITOR_TIME,
|
||||||
|
m_usageTimer.elapsed());
|
||||||
|
QQuickWidget::focusOutEvent(focusEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::focusInEvent(QFocusEvent *focusEvent)
|
||||||
|
{
|
||||||
|
m_usageTimer.restart();
|
||||||
|
QQuickWidget::focusInEvent(focusEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatesEditorWidget::reloadQmlSource()
|
||||||
|
{
|
||||||
|
QString statesListQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml");
|
||||||
|
QTC_ASSERT(QFileInfo::exists(statesListQmlFilePath), return );
|
||||||
|
engine()->clearComponentCache();
|
||||||
|
setSource(QUrl::fromLocalFile(statesListQmlFilePath));
|
||||||
|
|
||||||
|
if (!rootObject()) {
|
||||||
|
QString errorString;
|
||||||
|
for (const QQmlError &error : errors())
|
||||||
|
errorString += "\n" + error.toString();
|
||||||
|
|
||||||
|
Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"),
|
||||||
|
tr("StatesEditorWidget: %1 cannot be created.%2")
|
||||||
|
.arg(qmlSourcesPath(), errorString));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(rootObject(),
|
||||||
|
SIGNAL(currentStateInternalIdChanged()),
|
||||||
|
m_statesEditorView.data(),
|
||||||
|
SLOT(synchonizeCurrentStateFromWidget()));
|
||||||
|
connect(rootObject(),
|
||||||
|
SIGNAL(createNewState()),
|
||||||
|
m_statesEditorView.data(),
|
||||||
|
SLOT(createNewState()));
|
||||||
|
connect(rootObject(), SIGNAL(cloneState(int)), m_statesEditorView.data(), SLOT(cloneState(int)));
|
||||||
|
connect(rootObject(),
|
||||||
|
SIGNAL(extendState(int)),
|
||||||
|
m_statesEditorView.data(),
|
||||||
|
SLOT(extendState(int)));
|
||||||
|
connect(rootObject(),
|
||||||
|
SIGNAL(deleteState(int)),
|
||||||
|
m_statesEditorView.data(),
|
||||||
|
SLOT(removeState(int)));
|
||||||
|
m_statesEditorView.data()->synchonizeCurrentStateFromWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -0,0 +1,80 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QQmlPropertyMap>
|
||||||
|
#include <QQuickWidget>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QShortcut;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class NodeInstanceView;
|
||||||
|
|
||||||
|
namespace Experimental {
|
||||||
|
|
||||||
|
class StatesEditorModel;
|
||||||
|
class StatesEditorView;
|
||||||
|
|
||||||
|
namespace Internal { class StatesEditorImageProvider; }
|
||||||
|
|
||||||
|
class StatesEditorWidget : public QQuickWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
StatesEditorWidget(StatesEditorView *m_statesEditorView, StatesEditorModel *statesEditorModel);
|
||||||
|
~StatesEditorWidget() override;
|
||||||
|
|
||||||
|
int currentStateInternalId() const;
|
||||||
|
void setCurrentStateInternalId(int internalId);
|
||||||
|
void setNodeInstanceView(NodeInstanceView *nodeInstanceView);
|
||||||
|
|
||||||
|
void showAddNewStatesButton(bool showAddNewStatesButton);
|
||||||
|
|
||||||
|
static QString qmlSourcesPath();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *) override;
|
||||||
|
void focusOutEvent(QFocusEvent *focusEvent) override;
|
||||||
|
void focusInEvent(QFocusEvent *focusEvent) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reloadQmlSource();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<StatesEditorView> m_statesEditorView;
|
||||||
|
Internal::StatesEditorImageProvider *m_imageProvider;
|
||||||
|
QShortcut *m_qmlSourceUpdateShortcut;
|
||||||
|
QElapsedTimer m_usageTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Experimental
|
||||||
|
} // namespace QmlDesigner
|
@@ -24,6 +24,7 @@ QtcProduct {
|
|||||||
"../components/navigator",
|
"../components/navigator",
|
||||||
"../components/propertyeditor",
|
"../components/propertyeditor",
|
||||||
"../components/stateseditor",
|
"../components/stateseditor",
|
||||||
|
"../components/stateseditornew",
|
||||||
"../designercore",
|
"../designercore",
|
||||||
"../designercore/include",
|
"../designercore/include",
|
||||||
"../../../../share/qtcreator/qml/qmlpuppet/interfaces",
|
"../../../../share/qtcreator/qml/qmlpuppet/interfaces",
|
||||||
|
@@ -38,11 +38,15 @@ public:
|
|||||||
QmlModelStateOperation(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) {}
|
QmlModelStateOperation(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) {}
|
||||||
ModelNode target() const;
|
ModelNode target() const;
|
||||||
void setTarget(const ModelNode &target);
|
void setTarget(const ModelNode &target);
|
||||||
|
bool explicitValue() const;
|
||||||
|
void setExplicitValue(bool value);
|
||||||
|
bool restoreEntryValues() const;
|
||||||
|
void setRestoreEntryValues(bool value);
|
||||||
|
QList<AbstractProperty> targetProperties() const;
|
||||||
bool isValid() const override;
|
bool isValid() const override;
|
||||||
static bool isValidQmlModelStateOperation(const ModelNode &modelNode);
|
static bool isValidQmlModelStateOperation(const ModelNode &modelNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class QMLDESIGNERCORE_EXPORT QmlPropertyChanges : public QmlModelStateOperation
|
class QMLDESIGNERCORE_EXPORT QmlPropertyChanges : public QmlModelStateOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@@ -38,9 +38,14 @@ class Annotation;
|
|||||||
class AnnotationEditor;
|
class AnnotationEditor;
|
||||||
class StatesEditorView;
|
class StatesEditorView;
|
||||||
|
|
||||||
|
namespace Experimental {
|
||||||
|
class StatesEditorView;
|
||||||
|
}
|
||||||
|
|
||||||
class QMLDESIGNERCORE_EXPORT QmlModelState : public QmlModelNodeFacade
|
class QMLDESIGNERCORE_EXPORT QmlModelState : public QmlModelNodeFacade
|
||||||
{
|
{
|
||||||
friend StatesEditorView;
|
friend StatesEditorView;
|
||||||
|
friend Experimental::StatesEditorView;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QmlModelState();
|
QmlModelState();
|
||||||
@@ -82,6 +87,10 @@ public:
|
|||||||
bool hasAnnotation() const;
|
bool hasAnnotation() const;
|
||||||
void removeAnnotation();
|
void removeAnnotation();
|
||||||
|
|
||||||
|
QString extend() const;
|
||||||
|
void setExtend(const QString &name);
|
||||||
|
bool hasExtend() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void addChangeSetIfNotExists(const ModelNode &node);
|
void addChangeSetIfNotExists(const ModelNode &node);
|
||||||
static QmlModelState createBaseState(const AbstractView *view);
|
static QmlModelState createBaseState(const AbstractView *view);
|
||||||
|
@@ -122,6 +122,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelStateGroup
|
|||||||
{
|
{
|
||||||
friend class QmlObjectNode;
|
friend class QmlObjectNode;
|
||||||
friend class StatesEditorView;
|
friend class StatesEditorView;
|
||||||
|
friend class Experimental::StatesEditorView;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@@ -29,6 +29,8 @@
|
|||||||
#include "abstractview.h"
|
#include "abstractview.h"
|
||||||
#include <metainfo.h>
|
#include <metainfo.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
ModelNode QmlModelStateOperation::target() const
|
ModelNode QmlModelStateOperation::target() const
|
||||||
@@ -44,6 +46,40 @@ void QmlModelStateOperation::setTarget(const ModelNode &target)
|
|||||||
modelNode().bindingProperty("target").setExpression(target.id());
|
modelNode().bindingProperty("target").setExpression(target.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QmlModelStateOperation::explicitValue() const
|
||||||
|
{
|
||||||
|
if (modelNode().property("explicit").isVariantProperty())
|
||||||
|
return modelNode().variantProperty("explicit").value().toBool();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlModelStateOperation::setExplicitValue(bool value)
|
||||||
|
{
|
||||||
|
modelNode().variantProperty("explicit").setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QmlModelStateOperation::restoreEntryValues() const
|
||||||
|
{
|
||||||
|
if (modelNode().property("restoreEntryValues").isVariantProperty())
|
||||||
|
return modelNode().variantProperty("restoreEntryValues").value().toBool();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlModelStateOperation::setRestoreEntryValues(bool value)
|
||||||
|
{
|
||||||
|
modelNode().variantProperty("restoreEntryValues").setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<AbstractProperty> QmlModelStateOperation::targetProperties() const
|
||||||
|
{
|
||||||
|
return Utils::filtered(modelNode().properties(), [](const AbstractProperty &property) {
|
||||||
|
const QList<PropertyName> ignore = {"target", "explicit", "restoreEntryValues"};
|
||||||
|
return !ignore.contains(property.name());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool QmlPropertyChanges::isValid() const
|
bool QmlPropertyChanges::isValid() const
|
||||||
{
|
{
|
||||||
return isValidQmlPropertyChanges(modelNode());
|
return isValidQmlPropertyChanges(modelNode());
|
||||||
|
@@ -368,6 +368,28 @@ void QmlModelState::removeAnnotation()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QmlModelState::extend() const
|
||||||
|
{
|
||||||
|
if (isBaseState())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return modelNode().variantProperty("extend").value().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlModelState::setExtend(const QString &name)
|
||||||
|
{
|
||||||
|
if ((!isBaseState()) && (modelNode().isValid()))
|
||||||
|
modelNode().variantProperty("extend").setValue(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QmlModelState::hasExtend() const
|
||||||
|
{
|
||||||
|
if (!isBaseState() && modelNode().isValid())
|
||||||
|
return modelNode().hasVariantProperty("extend");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QmlModelState QmlModelState::createBaseState(const AbstractView *view)
|
QmlModelState QmlModelState::createBaseState(const AbstractView *view)
|
||||||
{
|
{
|
||||||
QmlModelState qmlModelState(view->rootModelNode());
|
QmlModelState qmlModelState(view->rootModelNode());
|
||||||
|
@@ -1960,7 +1960,7 @@ void ModelValidator::typeDiffers(bool /*isRootNode*/,
|
|||||||
Q_UNUSED(minorVersion)
|
Q_UNUSED(minorVersion)
|
||||||
Q_UNUSED(majorVersion)
|
Q_UNUSED(majorVersion)
|
||||||
|
|
||||||
QTC_ASSERT(modelNode.type() == typeName, return);
|
QTC_ASSERT(modelNode.type() == typeName, return );
|
||||||
|
|
||||||
if (modelNode.majorVersion() != majorVersion) {
|
if (modelNode.majorVersion() != majorVersion) {
|
||||||
qDebug() << Q_FUNC_INFO << modelNode;
|
qDebug() << Q_FUNC_INFO << modelNode;
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#ifndef QMLDESIGNER_TEST
|
#ifndef QMLDESIGNER_TEST
|
||||||
|
|
||||||
#include <abstractview.h>
|
#include <abstractview.h>
|
||||||
|
#include <assetslibraryview.h>
|
||||||
#include <capturingconnectionmanager.h>
|
#include <capturingconnectionmanager.h>
|
||||||
#include <componentaction.h>
|
#include <componentaction.h>
|
||||||
#include <componentview.h>
|
#include <componentview.h>
|
||||||
@@ -38,13 +39,13 @@
|
|||||||
#include <edit3dview.h>
|
#include <edit3dview.h>
|
||||||
#include <formeditorview.h>
|
#include <formeditorview.h>
|
||||||
#include <itemlibraryview.h>
|
#include <itemlibraryview.h>
|
||||||
#include <assetslibraryview.h>
|
#include <materialbrowserview.h>
|
||||||
|
#include <materialeditorview.h>
|
||||||
#include <navigatorview.h>
|
#include <navigatorview.h>
|
||||||
#include <nodeinstanceview.h>
|
#include <nodeinstanceview.h>
|
||||||
#include <propertyeditorview.h>
|
#include <propertyeditorview.h>
|
||||||
#include <materialeditorview.h>
|
|
||||||
#include <materialbrowserview.h>
|
|
||||||
#include <rewriterview.h>
|
#include <rewriterview.h>
|
||||||
|
#include <stateseditornew/stateseditorview.h>
|
||||||
#include <stateseditorview.h>
|
#include <stateseditorview.h>
|
||||||
#include <texteditorview.h>
|
#include <texteditorview.h>
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
@@ -86,6 +87,7 @@ public:
|
|||||||
MaterialEditorView materialEditorView;
|
MaterialEditorView materialEditorView;
|
||||||
MaterialBrowserView materialBrowserView;
|
MaterialBrowserView materialBrowserView;
|
||||||
StatesEditorView statesEditorView;
|
StatesEditorView statesEditorView;
|
||||||
|
Experimental::StatesEditorView newStatesEditorView;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<AbstractView>> additionalViews;
|
std::vector<std::unique_ptr<AbstractView>> additionalViews;
|
||||||
bool disableStandardViews = false;
|
bool disableStandardViews = false;
|
||||||
@@ -168,12 +170,22 @@ void ViewManager::switchStateEditorViewToBaseState()
|
|||||||
d->savedState = d->statesEditorView.currentState();
|
d->savedState = d->statesEditorView.currentState();
|
||||||
d->statesEditorView.setCurrentState(d->statesEditorView.baseState());
|
d->statesEditorView.setCurrentState(d->statesEditorView.baseState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Settings branch
|
||||||
|
if (d->newStatesEditorView.isAttached()) {
|
||||||
|
d->savedState = d->newStatesEditorView.currentState();
|
||||||
|
d->newStatesEditorView.setCurrentState(d->newStatesEditorView.baseState());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewManager::switchStateEditorViewToSavedState()
|
void ViewManager::switchStateEditorViewToSavedState()
|
||||||
{
|
{
|
||||||
if (d->savedState.isValid() && d->statesEditorView.isAttached())
|
if (d->savedState.isValid() && d->statesEditorView.isAttached())
|
||||||
d->statesEditorView.setCurrentState(d->savedState);
|
d->statesEditorView.setCurrentState(d->savedState);
|
||||||
|
|
||||||
|
// TODO Settings branch
|
||||||
|
if (d->savedState.isValid() && d->newStatesEditorView.isAttached())
|
||||||
|
d->newStatesEditorView.setCurrentState(d->savedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<AbstractView *> ViewManager::views() const
|
QList<AbstractView *> ViewManager::views() const
|
||||||
@@ -196,6 +208,7 @@ QList<AbstractView *> ViewManager::standardViews() const
|
|||||||
&d->materialEditorView,
|
&d->materialEditorView,
|
||||||
&d->materialBrowserView,
|
&d->materialBrowserView,
|
||||||
&d->statesEditorView,
|
&d->statesEditorView,
|
||||||
|
&d->newStatesEditorView, // TODO
|
||||||
&d->designerActionManagerView};
|
&d->designerActionManagerView};
|
||||||
|
|
||||||
if (QmlDesignerPlugin::instance()->settings().value(
|
if (QmlDesignerPlugin::instance()->settings().value(
|
||||||
@@ -331,6 +344,7 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
|
|||||||
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
||||||
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
||||||
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
||||||
|
widgetInfoList.append(d->newStatesEditorView.widgetInfo()); // TODO
|
||||||
if (d->debugView.hasWidget())
|
if (d->debugView.hasWidget())
|
||||||
widgetInfoList.append(d->debugView.widgetInfo());
|
widgetInfoList.append(d->debugView.widgetInfo());
|
||||||
|
|
||||||
|
@@ -112,6 +112,8 @@ const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150;
|
|||||||
const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
|
const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
|
||||||
const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
|
const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
|
||||||
const char EVENT_STATE_ADDED[] = "stateAdded";
|
const char EVENT_STATE_ADDED[] = "stateAdded";
|
||||||
|
const char EVENT_STATE_CLONED[] = "stateCloned";
|
||||||
|
const char EVENT_STATE_EXTENDED[] = "stateExtended";
|
||||||
const char EVENT_CONNECTION_ADDED[] = "connectionAdded";
|
const char EVENT_CONNECTION_ADDED[] = "connectionAdded";
|
||||||
const char EVENT_PROPERTY_ADDED[] = "propertyAdded";
|
const char EVENT_PROPERTY_ADDED[] = "propertyAdded";
|
||||||
const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
|
const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
|
||||||
|
@@ -59,6 +59,7 @@ Project {
|
|||||||
"components/navigator",
|
"components/navigator",
|
||||||
"components/pluginmanager",
|
"components/pluginmanager",
|
||||||
"components/stateseditor",
|
"components/stateseditor",
|
||||||
|
"components/stateseditornew",
|
||||||
"components/texteditor",
|
"components/texteditor",
|
||||||
"components/timelineeditor",
|
"components/timelineeditor",
|
||||||
"components/listmodeleditor",
|
"components/listmodeleditor",
|
||||||
@@ -777,6 +778,18 @@ Project {
|
|||||||
"stateseditor/stateseditorview.h",
|
"stateseditor/stateseditorview.h",
|
||||||
"stateseditor/stateseditorwidget.cpp",
|
"stateseditor/stateseditorwidget.cpp",
|
||||||
"stateseditor/stateseditorwidget.h",
|
"stateseditor/stateseditorwidget.h",
|
||||||
|
"stateseditornew/propertychangesmodel.cpp",
|
||||||
|
"stateseditornew/propertychangesmodel.h",
|
||||||
|
"stateseditornew/propertymodel.cpp",
|
||||||
|
"stateseditornew/propertymodel.h",
|
||||||
|
"stateseditornew/stateseditorimageprovider.cpp",
|
||||||
|
"stateseditornew/stateseditorimageprovider.h",
|
||||||
|
"stateseditornew/stateseditormodel.cpp",
|
||||||
|
"stateseditornew/stateseditormodel.h",
|
||||||
|
"stateseditornew/stateseditorview.cpp",
|
||||||
|
"stateseditornew/stateseditorview.h",
|
||||||
|
"stateseditornew/stateseditorwidget.cpp",
|
||||||
|
"stateseditornew/stateseditorwidget.h",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user