forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/8.0'
Conflicts: src/plugins/qmldesigner/designercore/include/qmlstate.h src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp Change-Id: Iefff97499db72727c1b8058d53b1279fd8853ad9
This commit is contained in:
839
share/qtcreator/qmldesigner/newstateseditor/Main.qml
Normal file
839
share/qtcreator/qmldesigner/newstateseditor/Main.qml
Normal file
@@ -0,0 +1,839 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
orientation: Qt.Horizontal
|
||||
}
|
||||
|
||||
ScrollBar.vertical: StateScrollBar {
|
||||
parent: scrollView
|
||||
x: scrollView.mirrored ? 0 : scrollView.width - width
|
||||
y: scrollView.topPadding
|
||||
height: scrollView.availableHeight
|
||||
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
|
||||
}
|
||||
flickableDirection: {
|
||||
if (frame.contentHeight <= scrollView.height)
|
||||
return Flickable.HorizontalFlick
|
||||
|
||||
if (frame.contentWidth <= scrollView.width)
|
||||
return Flickable.VerticalFlick
|
||||
|
||||
return Flickable.HorizontalAndVerticalFlick
|
||||
}
|
||||
|
||||
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 && !root.hasExtend
|
||||
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,70 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.ScrollBar {
|
||||
id: scrollBar
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding)
|
||||
|
||||
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.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
756
share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
Normal file
756
share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
Normal file
@@ -0,0 +1,756 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 {
|
||||
id: frame
|
||||
boundsMovement: Flickable.StopAtBounds
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
interactive: true
|
||||
contentWidth: column.width
|
||||
contentHeight: column.height
|
||||
flickableDirection: {
|
||||
if (frame.contentHeight <= scrollView.height)
|
||||
return Flickable.HorizontalFlick
|
||||
|
||||
if (frame.contentWidth <= scrollView.width)
|
||||
return Flickable.VerticalFlick
|
||||
|
||||
return Flickable.HorizontalAndVerticalFlick
|
||||
}
|
||||
|
||||
// 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
|
@@ -17,6 +17,7 @@ PropertyEditorPane {
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
visible: !hasMultiSelection
|
||||
}
|
||||
|
||||
GeometrySection {}
|
||||
|
@@ -20,6 +20,7 @@ PropertyEditorPane {
|
||||
|
||||
DynamicPropertiesSection {
|
||||
propertiesModel: SelectionDynamicPropertiesModel {}
|
||||
visible: !hasMultiSelection
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
@@ -677,6 +677,7 @@ Section {
|
||||
translationIndicatorVisible: false
|
||||
width: cePopup.itemWidth
|
||||
rightPadding: 8
|
||||
validator: RegExpValidator { regExp: /[a-z]+[0-9A-Za-z]*/ }
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
|
@@ -21,6 +21,9 @@ T.AbstractButton {
|
||||
property alias backgroundVisible: buttonBackground.visible
|
||||
property alias backgroundRadius: buttonBackground.radius
|
||||
|
||||
// Inverts the checked style
|
||||
property bool checkedInverted: false
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
@@ -76,11 +79,12 @@ T.AbstractButton {
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "select"
|
||||
name: "check"
|
||||
when: myButton.enabled && !myButton.pressed && myButton.checked
|
||||
PropertyChanges {
|
||||
target: buttonIcon
|
||||
color: StudioTheme.Values.themeIconColorSelected
|
||||
color: myButton.checkedInverted ? StudioTheme.Values.themeTextSelectedTextColor
|
||||
: StudioTheme.Values.themeIconColorSelected
|
||||
}
|
||||
},
|
||||
State {
|
||||
@@ -103,6 +107,7 @@ T.AbstractButton {
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
}
|
||||
PropertyChanges {
|
||||
target: myButton
|
||||
@@ -115,19 +120,32 @@ T.AbstractButton {
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hover"
|
||||
when: myButton.hover && !myButton.pressed && myButton.enabled
|
||||
when: !myButton.checked && myButton.hover && !myButton.pressed && myButton.enabled
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
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 {
|
||||
name: "press"
|
||||
when: myButton.hover && myButton.pressed
|
||||
when: myButton.hover && myButton.pressed && myButton.enabled
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
color: StudioTheme.Values.themeInteraction
|
||||
@@ -138,6 +156,17 @@ T.AbstractButton {
|
||||
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 {
|
||||
name: "disable"
|
||||
when: !myButton.enabled
|
||||
|
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.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,68 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.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()
|
||||
}
|
||||
}
|
@@ -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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates as T
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
T.ProgressBar {
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding)
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 6
|
||||
|
||||
Rectangle {
|
||||
width: control.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: StudioTheme.Values.themeInteraction
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 6
|
||||
color: StudioTheme.Values.themeThumbnailLabelBackground
|
||||
}
|
||||
}
|
@@ -8,22 +8,24 @@ import StudioTheme 1.0 as StudioTheme
|
||||
T.TextField {
|
||||
id: root
|
||||
|
||||
property alias actionIndicator: actionIndicator
|
||||
property alias translationIndicator: translationIndicator
|
||||
|
||||
// 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
|
||||
property bool edit: root.activeFocus
|
||||
|
||||
property alias actionIndicator: actionIndicator
|
||||
property alias actionIndicatorVisible: actionIndicator.visible
|
||||
property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
|
||||
property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
|
||||
|
||||
property alias translationIndicator: translationIndicator
|
||||
property alias translationIndicatorVisible: translationIndicator.visible
|
||||
property real __translationIndicatorWidth: StudioTheme.Values.translationIndicatorWidth
|
||||
property real __translationIndicatorHeight: StudioTheme.Values.translationIndicatorHeight
|
||||
|
||||
property alias indicator: indicator
|
||||
property alias indicatorVisible: indicator.visible
|
||||
|
||||
property string preFocusText: ""
|
||||
|
||||
horizontalAlignment: Qt.AlignLeft
|
||||
@@ -46,7 +48,7 @@ T.TextField {
|
||||
implicitHeight: StudioTheme.Values.defaultControlHeight
|
||||
|
||||
leftPadding: StudioTheme.Values.inputHorizontalPadding + actionIndicator.width
|
||||
rightPadding: StudioTheme.Values.inputHorizontalPadding + translationIndicator.width
|
||||
rightPadding: StudioTheme.Values.inputHorizontalPadding + translationIndicator.width + indicator.width
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
@@ -122,6 +124,14 @@ T.TextField {
|
||||
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 {
|
||||
id: translationIndicator
|
||||
myControl: root
|
||||
@@ -151,8 +161,8 @@ T.TextField {
|
||||
},
|
||||
State {
|
||||
name: "globalHover"
|
||||
when: (actionIndicator.hover || translationIndicator.hover) && !root.edit
|
||||
&& root.enabled
|
||||
when: (actionIndicator.hover || translationIndicator.hover || indicator.hover)
|
||||
&& !root.edit && root.enabled
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundGlobalHover
|
||||
@@ -167,7 +177,7 @@ T.TextField {
|
||||
State {
|
||||
name: "hover"
|
||||
when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover
|
||||
&& !root.edit && root.enabled
|
||||
&& !indicator.hover && !root.edit && root.enabled
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundHover
|
||||
|
@@ -8,7 +8,11 @@ CheckIndicator 1.0 CheckIndicator.qml
|
||||
ComboBox 1.0 ComboBox.qml
|
||||
ComboBoxInput 1.0 ComboBoxInput.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
|
||||
Indicator 1.0 Indicator.qml
|
||||
InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml
|
||||
ItemDelegate 1.0 ItemDelegate.qml
|
||||
LinkIndicator2D 1.0 LinkIndicator2D.qml
|
||||
@@ -18,6 +22,7 @@ Menu 1.0 Menu.qml
|
||||
MenuItem 1.0 MenuItem.qml
|
||||
MenuItemWithIcon 1.0 MenuItemWithIcon.qml
|
||||
MenuSeparator 1.0 MenuSeparator.qml
|
||||
ProgressBar 1.0 ProgressBar.qml
|
||||
RadioButton 1.0 RadioButton.qml
|
||||
RealSliderPopup 1.0 RealSliderPopup.qml
|
||||
RealSpinBox 1.0 RealSpinBox.qml
|
||||
|
@@ -194,6 +194,15 @@ QtObject {
|
||||
property real colorEditorPopupCmoboBoxWidth: 110
|
||||
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
|
||||
|
||||
property bool isLightTheme: themeControlBackground.hsvValue > themeTextColor.hsvValue
|
||||
@@ -266,7 +275,7 @@ QtObject {
|
||||
|
||||
// Slider colors
|
||||
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 themeSliderInactiveTrack: Theme.color(Theme.DSsliderInactiveTrack)
|
||||
property string themeSliderInactiveTrackHover: Theme.color(Theme.DSsliderInactiveTrackHover)
|
||||
@@ -286,10 +295,15 @@ QtObject {
|
||||
property string themeTabInactiveBackground: Theme.color(Theme.DStabInactiveBackground)
|
||||
property string themeTabInactiveText: Theme.color(Theme.DStabInactiveText)
|
||||
|
||||
// State Editor
|
||||
property string themeStateSeparator: Theme.color(Theme.DSstateSeparatorColor)
|
||||
property string themeStateBackground: Theme.color(Theme.DSstateBackgroundColor)
|
||||
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)
|
||||
|
||||
// Taken out of Constants.js
|
||||
@@ -312,9 +326,13 @@ QtObject {
|
||||
property string themeListItemTextHover: Theme.color(Theme.DSnavigatorTextHover)
|
||||
property string themeListItemTextPress: Theme.color(Theme.DSnavigatorTextSelected)
|
||||
|
||||
//Welcome Page
|
||||
// Welcome Page
|
||||
property string welcomeScreenBackground: Theme.color(Theme.DSwelcomeScreenBackground)
|
||||
property string themeSubPanelBackground: Theme.color(Theme.DSsubPanelBackground)
|
||||
property string themeThumbnailBackground: Theme.color(Theme.DSthumbnailBackground)
|
||||
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
|
||||
DSstatePreviewOutline=ffaaaaaa
|
||||
|
||||
DSstatePanelBackground=ff252525
|
||||
DSstateHighlight=ff727272
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -85,6 +85,9 @@ DSstateSeparatorColor=ffadadad
|
||||
DSstateBackgroundColor=ffe0e0e0
|
||||
DSstatePreviewOutline=ff363636
|
||||
|
||||
DSstatePanelBackground=ffdadada
|
||||
DSstateHighlight=ff8d8d8d
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -99,6 +99,9 @@ DSstateSeparatorColor=ffadadad
|
||||
DSstateBackgroundColor=ffe0e0e0
|
||||
DSstatePreviewOutline=ff363636
|
||||
|
||||
DSstatePanelBackground=ffdadada
|
||||
DSstateHighlight=ff8d8d8d
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -96,6 +96,9 @@ DSstateSeparatorColor=ff7c7b7b
|
||||
DSstateBackgroundColor=ff383838
|
||||
DSstatePreviewOutline=ffaaaaaa
|
||||
|
||||
DSstatePanelBackground=ff252525
|
||||
DSstateHighlight=ff727272
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -98,6 +98,9 @@ DSstateSeparatorColor=ff7c7b7b
|
||||
DSstateBackgroundColor=ff383838
|
||||
DSstatePreviewOutline=ffaaaaaa
|
||||
|
||||
DSstatePanelBackground=ff252525
|
||||
DSstateHighlight=ff727272
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -94,6 +94,9 @@ DSstateSeparatorColor=ffadadad
|
||||
DSstateBackgroundColor=ffe0e0e0
|
||||
DSstatePreviewOutline=ff363636
|
||||
|
||||
DSstatePanelBackground=ffdadada
|
||||
DSstateHighlight=ff8d8d8d
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -92,6 +92,9 @@ DSstateSeparatorColor=ff7c7b7b
|
||||
DSstateBackgroundColor=ff383838
|
||||
DSstatePreviewOutline=ffaaaaaa
|
||||
|
||||
DSstatePanelBackground=ff252525
|
||||
DSstateHighlight=ff727272
|
||||
|
||||
DSchangedStateText=ff99ccff
|
||||
|
||||
DS3DAxisXColor=ffd00000
|
||||
|
@@ -237,7 +237,7 @@ DeviceShell::RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray
|
||||
const int id = ++m_currentId;
|
||||
const auto it = m_commandOutput.insert(id, CommandRun{{-1, {}, {}}, &waiter});
|
||||
|
||||
QMetaObject::invokeMethod(m_shellProcess, [this, id, &cmd, &stdInData]() {
|
||||
QMetaObject::invokeMethod(m_shellProcess, [this, id, cmd, stdInData]() {
|
||||
const QString command = QString("%1 \"%2\" %3\n")
|
||||
.arg(id)
|
||||
.arg(QString::fromLatin1(stdInData.toBase64()))
|
||||
|
@@ -416,6 +416,9 @@ public:
|
||||
DSgreenLight,
|
||||
DSamberLight,
|
||||
DSredLight,
|
||||
|
||||
DSstatePanelBackground,
|
||||
DSstateHighlight,
|
||||
};
|
||||
|
||||
enum ImageFile {
|
||||
|
@@ -424,6 +424,17 @@ extend_qtc_plugin(QmlDesigner
|
||||
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
|
||||
SOURCES_PREFIX components/texteditor
|
||||
SOURCES
|
||||
|
@@ -410,6 +410,20 @@ QQmlComponent *PropertyEditorContextObject::specificQmlComponent()
|
||||
return m_qmlComponent;
|
||||
}
|
||||
|
||||
bool PropertyEditorContextObject::hasMultiSelection() const
|
||||
{
|
||||
return m_hasMultiSelection;
|
||||
}
|
||||
|
||||
void PropertyEditorContextObject::setHasMultiSelection(bool b)
|
||||
{
|
||||
if (b == m_hasMultiSelection)
|
||||
return;
|
||||
|
||||
m_hasMultiSelection = b;
|
||||
emit hasMultiSelectionChanged();
|
||||
}
|
||||
|
||||
void PropertyEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl)
|
||||
{
|
||||
if (newSpecificsUrl == m_specificsUrl)
|
||||
|
@@ -46,6 +46,9 @@ class PropertyEditorContextObject : public QObject
|
||||
|
||||
Q_PROPERTY(QQmlComponent* specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged)
|
||||
|
||||
Q_PROPERTY(bool hasMultiSelection READ hasMultiSelection WRITE setHasMultiSelection NOTIFY
|
||||
hasMultiSelectionChanged)
|
||||
|
||||
public:
|
||||
PropertyEditorContextObject(QObject *parent = nullptr);
|
||||
|
||||
@@ -104,6 +107,10 @@ public:
|
||||
|
||||
bool hasAliasExport() const { return m_aliasExport; }
|
||||
|
||||
bool hasMultiSelection() const;
|
||||
|
||||
void setHasMultiSelection(bool);
|
||||
|
||||
signals:
|
||||
void specificsUrlChanged();
|
||||
void specificQmlDataChanged();
|
||||
@@ -120,6 +127,7 @@ signals:
|
||||
void hasAliasExportChanged();
|
||||
void hasActiveTimelineChanged();
|
||||
void activeDragSuffixChanged();
|
||||
void hasMultiSelectionChanged();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -170,6 +178,8 @@ private:
|
||||
bool m_setHasActiveTimeline = false;
|
||||
|
||||
QString m_activeDragSuffix;
|
||||
|
||||
bool m_hasMultiSelection = false;
|
||||
};
|
||||
|
||||
class EasingCurveEditor : public QObject
|
||||
|
@@ -75,7 +75,6 @@ PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache)
|
||||
|
||||
m_stackedWidget->insertWidget(0, new QWidget(m_stackedWidget));
|
||||
|
||||
Quick2PropertyEditorView::registerQmlTypes();
|
||||
m_stackedWidget->setWindowTitle(tr("Properties"));
|
||||
}
|
||||
|
||||
|
@@ -14,10 +14,12 @@
|
||||
#include "gradientpresetcustomlistmodel.h"
|
||||
#include "gradientpresetdefaultlistmodel.h"
|
||||
#include "itemfiltermodel.h"
|
||||
#include "propertychangesmodel.h"
|
||||
#include "propertyeditorcontextobject.h"
|
||||
#include "propertyeditorimageprovider.h"
|
||||
#include "propertyeditorqmlbackend.h"
|
||||
#include "propertyeditorvalue.h"
|
||||
#include "propertymodel.h"
|
||||
#include "qmlanchorbindingproxy.h"
|
||||
#include "richtexteditor/richtexteditorproxy.h"
|
||||
#include "theme.h"
|
||||
@@ -56,6 +58,8 @@ void Quick2PropertyEditorView::registerQmlTypes()
|
||||
RichTextEditorProxy::registerDeclarativeType();
|
||||
SelectionDynamicPropertiesProxyModel::registerDeclarativeType();
|
||||
DynamicPropertyRow::registerDeclarativeType();
|
||||
Experimental::PropertyChangesModel::registerDeclarativeType();
|
||||
Experimental::PropertyModel::registerDeclarativeType();
|
||||
|
||||
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,173 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 {};
|
||||
// return propertyChanges.target().metaInfo().propertyType(
|
||||
// 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,457 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
{
|
||||
return {};
|
||||
// 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
|
||||
{
|
||||
return {};
|
||||
// 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,937 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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(),
|
||||
"StatesEditor",
|
||||
WidgetInfo::BottomPane,
|
||||
0,
|
||||
tr("States"));
|
||||
}
|
||||
|
||||
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 1;
|
||||
// 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/propertyeditor",
|
||||
"../components/stateseditor",
|
||||
"../components/stateseditornew",
|
||||
"../designercore",
|
||||
"../designercore/include",
|
||||
"../../../../share/qtcreator/qml/qmlpuppet/interfaces",
|
||||
|
@@ -9,20 +9,24 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT QmlModelStateOperation : public QmlModelNodeFacade
|
||||
class QMLDESIGNERCORE_EXPORT QmlModelStateOperation : public QmlModelNodeFacade
|
||||
{
|
||||
public:
|
||||
QmlModelStateOperation() : QmlModelNodeFacade() {}
|
||||
QmlModelStateOperation(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode) {}
|
||||
ModelNode target() const;
|
||||
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;
|
||||
explicit operator bool() const { return isValid(); }
|
||||
static bool isValidQmlModelStateOperation(const ModelNode &modelNode);
|
||||
};
|
||||
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT QmlPropertyChanges : public QmlModelStateOperation
|
||||
class QMLDESIGNERCORE_EXPORT QmlPropertyChanges : public QmlModelStateOperation
|
||||
{
|
||||
public:
|
||||
QmlPropertyChanges() : QmlModelStateOperation() {}
|
||||
|
@@ -121,6 +121,8 @@ public:
|
||||
|
||||
QList<ModelNode> allTimelines() const;
|
||||
|
||||
QList<ModelNode> getAllConnections() const;
|
||||
|
||||
protected:
|
||||
NodeInstance nodeInstance() const;
|
||||
QmlObjectNode nodeForInstance(const NodeInstance &instance) const;
|
||||
|
@@ -16,9 +16,14 @@ class Annotation;
|
||||
class AnnotationEditor;
|
||||
class StatesEditorView;
|
||||
|
||||
namespace Experimental {
|
||||
class StatesEditorView;
|
||||
}
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT QmlModelState final : public QmlModelNodeFacade
|
||||
{
|
||||
friend StatesEditorView;
|
||||
friend Experimental::StatesEditorView;
|
||||
|
||||
public:
|
||||
QmlModelState();
|
||||
@@ -61,6 +66,10 @@ public:
|
||||
bool hasAnnotation() const;
|
||||
void removeAnnotation();
|
||||
|
||||
QString extend() const;
|
||||
void setExtend(const QString &name);
|
||||
bool hasExtend() const;
|
||||
|
||||
protected:
|
||||
void addChangeSetIfNotExists(const ModelNode &node);
|
||||
static QmlModelState createBaseState(const AbstractView *view);
|
||||
|
@@ -103,6 +103,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelStateGroup
|
||||
{
|
||||
friend class QmlObjectNode;
|
||||
friend class StatesEditorView;
|
||||
friend class Experimental::StatesEditorView;
|
||||
|
||||
public:
|
||||
QmlModelStateGroup() = default;
|
||||
|
@@ -7,6 +7,8 @@
|
||||
#include "abstractview.h"
|
||||
#include <metainfo.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
ModelNode QmlModelStateOperation::target() const
|
||||
@@ -22,6 +24,40 @@ void QmlModelStateOperation::setTarget(const ModelNode &target)
|
||||
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
|
||||
{
|
||||
return isValidQmlPropertyChanges(modelNode());
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <qmldesignerplugin.h>
|
||||
#endif
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
@@ -593,6 +594,18 @@ QList<ModelNode> QmlObjectNode::allTimelines() const
|
||||
return timelineNodes;
|
||||
}
|
||||
|
||||
QList<ModelNode> QmlObjectNode::getAllConnections() const
|
||||
{
|
||||
// if (!isValid())
|
||||
return {};
|
||||
|
||||
// auto list = view()->allModelNodesOfType("QtQuick.Connections");
|
||||
// return Utils::filtered(list, [this](const ModelNode &connection) {
|
||||
// return connection.hasBindingProperty("target")
|
||||
// && connection.bindingProperty("target").resolveToModelNode() == modelNode();
|
||||
// });
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes a variant property of the object specified by \a name from the
|
||||
model.
|
||||
|
@@ -22,7 +22,7 @@ QmlModelState::QmlModelState()
|
||||
}
|
||||
|
||||
QmlModelState::QmlModelState(const ModelNode &modelNode)
|
||||
: QmlModelNodeFacade(modelNode)
|
||||
: QmlModelNodeFacade(modelNode)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ QList<QmlModelStateOperation> QmlModelState::stateOperations(const ModelNode &no
|
||||
{
|
||||
QList<QmlModelStateOperation> returnList;
|
||||
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
const QList<ModelNode> nodes = modelNode().nodeListProperty("changes").toModelNodeList();
|
||||
for (const ModelNode &childNode : nodes) {
|
||||
if (QmlModelStateOperation::isValidQmlModelStateOperation(childNode)) {
|
||||
@@ -67,7 +67,7 @@ QList<QmlPropertyChanges> QmlModelState::propertyChanges() const
|
||||
{
|
||||
QList<QmlPropertyChanges> returnList;
|
||||
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
const QList<ModelNode> nodes = modelNode().nodeListProperty("changes").toModelNodeList();
|
||||
for (const ModelNode &childNode : nodes) {
|
||||
//### exception if not valid QmlModelStateOperation
|
||||
@@ -82,7 +82,7 @@ QList<QmlPropertyChanges> QmlModelState::propertyChanges() const
|
||||
|
||||
bool QmlModelState::hasPropertyChanges(const ModelNode &node) const
|
||||
{
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
const QList<QmlPropertyChanges> changes = propertyChanges();
|
||||
for (const QmlPropertyChanges &changeSet : changes) {
|
||||
if (changeSet.target().isValid() && changeSet.target() == node)
|
||||
@@ -110,7 +110,7 @@ QList<QmlModelStateOperation> QmlModelState::stateOperations() const
|
||||
//### exception if not valid
|
||||
QList<QmlModelStateOperation> returnList;
|
||||
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
if (!isBaseState() && modelNode().hasNodeListProperty("changes")) {
|
||||
const QList<ModelNode> nodes = modelNode().nodeListProperty("changes").toModelNodeList();
|
||||
for (const ModelNode &childNode : nodes) {
|
||||
//### exception if not valid QmlModelStateOperation
|
||||
@@ -347,6 +347,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(view->rootModelNode());
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#ifndef QMLDESIGNER_TEST
|
||||
|
||||
#include <abstractview.h>
|
||||
#include <assetslibraryview.h>
|
||||
#include <capturingconnectionmanager.h>
|
||||
#include <componentaction.h>
|
||||
#include <componentview.h>
|
||||
@@ -16,13 +17,13 @@
|
||||
#include <edit3dview.h>
|
||||
#include <formeditorview.h>
|
||||
#include <itemlibraryview.h>
|
||||
#include <assetslibraryview.h>
|
||||
#include <materialbrowserview.h>
|
||||
#include <materialeditorview.h>
|
||||
#include <navigatorview.h>
|
||||
#include <nodeinstanceview.h>
|
||||
#include <propertyeditorview.h>
|
||||
#include <materialeditorview.h>
|
||||
#include <materialbrowserview.h>
|
||||
#include <rewriterview.h>
|
||||
#include <stateseditornew/stateseditorview.h>
|
||||
#include <stateseditorview.h>
|
||||
#include <texteditorview.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
@@ -35,6 +36,14 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
static bool useOldStatesEditor()
|
||||
{
|
||||
return QmlDesignerPlugin::instance()
|
||||
->settings()
|
||||
.value(DesignerSettingsKey::OLD_STATES_EDITOR)
|
||||
.toBool();
|
||||
}
|
||||
|
||||
static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg)
|
||||
|
||||
class ViewManagerData
|
||||
@@ -64,6 +73,7 @@ public:
|
||||
MaterialEditorView materialEditorView;
|
||||
MaterialBrowserView materialBrowserView;
|
||||
StatesEditorView statesEditorView;
|
||||
Experimental::StatesEditorView newStatesEditorView;
|
||||
|
||||
std::vector<std::unique_ptr<AbstractView>> additionalViews;
|
||||
bool disableStandardViews = false;
|
||||
@@ -142,16 +152,30 @@ void ViewManager::detachRewriterView()
|
||||
|
||||
void ViewManager::switchStateEditorViewToBaseState()
|
||||
{
|
||||
if (d->statesEditorView.isAttached()) {
|
||||
d->savedState = d->statesEditorView.currentState();
|
||||
d->statesEditorView.setCurrentState(d->statesEditorView.baseState());
|
||||
if (useOldStatesEditor()) {
|
||||
if (d->statesEditorView.isAttached()) {
|
||||
d->savedState = d->statesEditorView.currentState();
|
||||
d->statesEditorView.setCurrentState(d->statesEditorView.baseState());
|
||||
}
|
||||
} else {
|
||||
// TODO remove old statesview
|
||||
if (d->newStatesEditorView.isAttached()) {
|
||||
d->savedState = d->newStatesEditorView.currentState();
|
||||
d->newStatesEditorView.setCurrentState(d->newStatesEditorView.baseState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewManager::switchStateEditorViewToSavedState()
|
||||
{
|
||||
if (d->savedState.isValid() && d->statesEditorView.isAttached())
|
||||
d->statesEditorView.setCurrentState(d->savedState);
|
||||
if (useOldStatesEditor()) {
|
||||
if (d->savedState.isValid() && d->statesEditorView.isAttached())
|
||||
d->statesEditorView.setCurrentState(d->savedState);
|
||||
} else {
|
||||
// TODO remove old statesview
|
||||
if (d->savedState.isValid() && d->newStatesEditorView.isAttached())
|
||||
d->newStatesEditorView.setCurrentState(d->savedState);
|
||||
}
|
||||
}
|
||||
|
||||
QList<AbstractView *> ViewManager::views() const
|
||||
@@ -174,11 +198,19 @@ QList<AbstractView *> ViewManager::standardViews() const
|
||||
&d->materialEditorView,
|
||||
&d->materialBrowserView,
|
||||
&d->statesEditorView,
|
||||
&d->newStatesEditorView, // TODO
|
||||
&d->designerActionManagerView};
|
||||
|
||||
if (QmlDesignerPlugin::instance()->settings().value(
|
||||
DesignerSettingsKey::ENABLE_DEBUGVIEW).toBool())
|
||||
list.append(&d->debugView);
|
||||
if (useOldStatesEditor())
|
||||
list.removeAll(&d->newStatesEditorView);
|
||||
else
|
||||
list.removeAll(&d->statesEditorView);
|
||||
|
||||
if (QmlDesignerPlugin::instance()
|
||||
->settings()
|
||||
.value(DesignerSettingsKey::ENABLE_DEBUGVIEW)
|
||||
.toBool())
|
||||
list.append(&d->debugView);
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -308,7 +340,11 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
|
||||
widgetInfoList.append(d->propertyEditorView.widgetInfo());
|
||||
widgetInfoList.append(d->materialEditorView.widgetInfo());
|
||||
widgetInfoList.append(d->materialBrowserView.widgetInfo());
|
||||
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
||||
if (useOldStatesEditor())
|
||||
widgetInfoList.append(d->statesEditorView.widgetInfo());
|
||||
else
|
||||
widgetInfoList.append(d->newStatesEditorView.widgetInfo());
|
||||
|
||||
if (d->debugView.hasWidget())
|
||||
widgetInfoList.append(d->debugView.widgetInfo());
|
||||
|
||||
|
@@ -63,6 +63,7 @@ void DesignerSettings::fromSettings(QSettings *settings)
|
||||
restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa");
|
||||
restoreValue(settings, DesignerSettingsKey::SMOOTH_RENDERING, false);
|
||||
restoreValue(settings, DesignerSettingsKey::SHOW_DEBUG_SETTINGS, false);
|
||||
restoreValue(settings, DesignerSettingsKey::OLD_STATES_EDITOR, true);
|
||||
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
|
@@ -52,6 +52,7 @@ const char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode";
|
||||
const char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer";
|
||||
const char ASK_BEFORE_DELETING_ASSET[] = "AskBeforeDeletingAsset";
|
||||
const char SMOOTH_RENDERING[] = "SmoothRendering";
|
||||
const char OLD_STATES_EDITOR[] = "OldStatesEditor";
|
||||
}
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT DesignerSettings : public QHash<QByteArray, QVariant>
|
||||
|
@@ -90,6 +90,8 @@ const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150;
|
||||
const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
|
||||
const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
|
||||
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_PROPERTY_ADDED[] = "propertyAdded";
|
||||
const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
|
||||
|
@@ -4,14 +4,15 @@
|
||||
#include "qmldesignerplugin.h"
|
||||
#include "designmodecontext.h"
|
||||
#include "designmodewidget.h"
|
||||
#include "dynamiclicensecheck.h"
|
||||
#include "exception.h"
|
||||
#include "generateresource.h"
|
||||
#include "nodeinstanceview.h"
|
||||
#include "openuiqmlfiledialog.h"
|
||||
#include "qmldesignerconstants.h"
|
||||
#include "qmldesignerprojectmanager.h"
|
||||
#include "quick2propertyeditorview.h"
|
||||
#include "settingspage.h"
|
||||
#include "dynamiclicensecheck.h"
|
||||
|
||||
#include <metainfo.h>
|
||||
#include <connectionview.h>
|
||||
@@ -258,7 +259,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
|
||||
|
||||
designerActionManager().addDesignerAction(shutDownNanotraceAction);
|
||||
#endif
|
||||
|
||||
//TODO Move registering those types out of the property editor, since they are used also in the states editor
|
||||
Quick2PropertyEditorView::registerQmlTypes();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -60,6 +60,7 @@ Project {
|
||||
"components/navigator",
|
||||
"components/pluginmanager",
|
||||
"components/stateseditor",
|
||||
"components/stateseditornew",
|
||||
"components/texteditor",
|
||||
"components/timelineeditor",
|
||||
"components/listmodeleditor",
|
||||
@@ -821,6 +822,18 @@ Project {
|
||||
"stateseditor/stateseditorview.h",
|
||||
"stateseditor/stateseditorwidget.cpp",
|
||||
"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