forked from qt-creator/qt-creator
QmlDesigner: Add control for material management
- Create EditableListView for material management - Add id list functionality to PropertyEditorValue Task-number: QDS-1258 Task-number: QDS-1256 Change-Id: I1694648b36845b22f4773ecbd578d77c22c934e2 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Henning Gründl
parent
c6ab1d8b4f
commit
f4d6300b0b
@@ -0,0 +1,213 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Rectangle {
|
||||
id: editableListView
|
||||
|
||||
property variant backendValue
|
||||
|
||||
ExtendedFunctionLogic {
|
||||
id: extFuncLogic
|
||||
backendValue: editableListView.backendValue
|
||||
}
|
||||
|
||||
property var model
|
||||
onModelChanged: myRepeater.updateModel()
|
||||
|
||||
signal add(string value)
|
||||
signal remove(int idx)
|
||||
signal replace(int idx, string value)
|
||||
|
||||
property alias actionIndicator: actionIndicator
|
||||
|
||||
property alias actionIndicatorVisible: actionIndicator.visible
|
||||
property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth
|
||||
property real __actionIndicatorHeight: StudioTheme.Values.height
|
||||
|
||||
color: "transparent"
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
border.width: StudioTheme.Values.border
|
||||
|
||||
property int numVisibleItems: myRepeater.count
|
||||
|
||||
Layout.preferredWidth: StudioTheme.Values.height * 10
|
||||
Layout.preferredHeight: myColumn.height
|
||||
|
||||
Component {
|
||||
id: myDelegate
|
||||
ListViewComboBox {
|
||||
id: itemFilterComboBox
|
||||
|
||||
property int myIndex: index
|
||||
property bool empty: itemFilterComboBox.initialModelData === ""
|
||||
|
||||
validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ }
|
||||
|
||||
actionIndicatorVisible: false
|
||||
typeFilter: "QtQuick3D.Material"
|
||||
editText: modelData
|
||||
initialModelData: modelData
|
||||
|
||||
width: editableListView.width
|
||||
|
||||
onFocusChanged: {
|
||||
if (itemFilterComboBox.focus) {
|
||||
myColumn.currentIndex = index
|
||||
}
|
||||
|
||||
if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
|
||||
myRepeater.dirty = false
|
||||
editableListView.add(itemFilterComboBox.editText)
|
||||
}
|
||||
}
|
||||
|
||||
onCompressedActivated: {
|
||||
if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
|
||||
myRepeater.dirty = false
|
||||
editableListView.add(itemFilterComboBox.editText)
|
||||
} else {
|
||||
editableListView.replace(myIndex, itemFilterComboBox.editText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: highlightRect
|
||||
color: "transparent"
|
||||
border.width: StudioTheme.Values.border
|
||||
border.color: StudioTheme.Values.themeInteraction
|
||||
x: myColumn.currentItem ? myColumn.currentItem.x : 0
|
||||
y: myColumn.currentItem ? myColumn.currentItem.y : 0
|
||||
z: 10
|
||||
width: myColumn.currentItem ? myColumn.currentItem.width : 0
|
||||
height: myColumn.currentItem ? myColumn.currentItem.height : 0
|
||||
}
|
||||
|
||||
Column {
|
||||
id: myColumn
|
||||
|
||||
property int currentIndex: -1
|
||||
property Item currentItem
|
||||
|
||||
spacing: -1
|
||||
|
||||
onCurrentIndexChanged: myColumn.currentItem = myRepeater.itemAt(myColumn.currentIndex)
|
||||
|
||||
Repeater {
|
||||
id: myRepeater
|
||||
|
||||
property bool dirty: false
|
||||
property var localModel: []
|
||||
|
||||
delegate: myDelegate
|
||||
|
||||
onItemAdded: function(index, item) {
|
||||
if (index === myColumn.currentIndex)
|
||||
myColumn.currentItem = item
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
var lastIndex = myColumn.currentIndex
|
||||
myColumn.currentIndex = -1
|
||||
myRepeater.localModel = []
|
||||
|
||||
editableListView.model.forEach(function(item) {
|
||||
myRepeater.localModel.push(item)
|
||||
});
|
||||
|
||||
// if list view is still dirty, then last state had an unfinished/empty ComboBox
|
||||
if (myRepeater.dirty)
|
||||
myRepeater.localModel.push("")
|
||||
|
||||
myRepeater.model = myRepeater.localModel // trigger on change handler
|
||||
|
||||
if (lastIndex < 0 && myRepeater.localModel.length > 0)
|
||||
myColumn.currentIndex = 0
|
||||
else if (myRepeater.localModel.length > lastIndex)
|
||||
myColumn.currentIndex = lastIndex
|
||||
else
|
||||
myColumn.currentIndex = myRepeater.localModel.length - 1
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
spacing: -StudioTheme.Values.border
|
||||
|
||||
StudioControls.ActionIndicator {
|
||||
id: actionIndicator
|
||||
width: actionIndicator.visible ? __actionIndicatorWidth : 0
|
||||
height: actionIndicator.visible ? __actionIndicatorHeight : 0
|
||||
showBackground: true
|
||||
|
||||
icon.color: extFuncLogic.color
|
||||
icon.text: extFuncLogic.glyph
|
||||
onClicked: extFuncLogic.show()
|
||||
}
|
||||
StudioControls.AbstractButton {
|
||||
buttonIcon: "+"
|
||||
iconFont: StudioTheme.Constants.font
|
||||
enabled: !myRepeater.dirty
|
||||
onClicked: {
|
||||
var idx = myRepeater.localModel.push("") - 1
|
||||
myRepeater.model = myRepeater.localModel // trigger on change handler
|
||||
myRepeater.dirty = true
|
||||
myColumn.currentIndex = idx
|
||||
myColumn.currentItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
StudioControls.AbstractButton {
|
||||
buttonIcon: "-"
|
||||
iconFont: StudioTheme.Constants.font
|
||||
enabled: myRepeater.model.length
|
||||
onClicked: {
|
||||
var lastItem = myColumn.currentIndex === myRepeater.localModel.length - 1
|
||||
if (myColumn.currentItem.initialModelData === "") {
|
||||
myRepeater.localModel.pop()
|
||||
myRepeater.dirty = false
|
||||
myRepeater.model = myRepeater.localModel // trigger on change handler
|
||||
} else {
|
||||
editableListView.remove(myColumn.currentIndex)
|
||||
}
|
||||
if (!lastItem)
|
||||
myColumn.currentIndex = myColumn.currentIndex - 1
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.width: StudioTheme.Values.border
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
height: StudioTheme.Values.height
|
||||
width: editableListView.width - (StudioTheme.Values.height - StudioTheme.Values.border) * (actionIndicatorVisible ? 3 : 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Quick 3D.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL$
|
||||
** 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 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.12
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
|
||||
StudioControls.ComboBox {
|
||||
id: comboBox
|
||||
|
||||
property alias typeFilter: itemFilterModel.typeFilter
|
||||
|
||||
property var initialModelData
|
||||
property bool __isCompleted: false
|
||||
|
||||
editable: true
|
||||
model: itemFilterModel.itemModel
|
||||
|
||||
HelperWidgets.ItemFilterModel {
|
||||
id: itemFilterModel
|
||||
modelNodeBackendProperty: modelNodeBackend
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
comboBox.__isCompleted = true
|
||||
|
||||
// Workaround for proper initialization. Use the initial modelData value and search for it
|
||||
// in the model. If nothing was found, set the editText to the initial modelData.
|
||||
comboBox.currentIndex = comboBox.find(comboBox.initialModelData)
|
||||
|
||||
if (comboBox.currentIndex === -1)
|
||||
comboBox.editText = comboBox.initialModelData
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ ComboBox 2.0 ComboBox.qml
|
||||
CustomCheckBoxStyle 2.0 CustomCheckBoxStyle.qml
|
||||
CustomComboBoxStyle 2.0 CustomComboBoxStyle.qml
|
||||
CustomSpinBoxStyle 2.0 CustomSpinBoxStyle.qml
|
||||
EditableListView 2.0 EditableListView.qml
|
||||
ExpandingSpacer 2.0 ExpandingSpacer.qml
|
||||
ExtendedFunctionButton 2.0 ExtendedFunctionButton.qml
|
||||
ExtendedFunctionLogic 2.0 ExtendedFunctionLogic.qml
|
||||
@@ -30,6 +31,7 @@ GradientPresetTabContent 2.0 GradientPresetTabContent.qml
|
||||
GroupBox 2.0 GroupBox.qml
|
||||
HueSlider 2.0 HueSlider.qml
|
||||
IconLabel 2.0 IconLabel.qml
|
||||
ListViewComboBox 2.0 ListViewComboBox.qml
|
||||
Label 2.0 Label.qml
|
||||
LineEdit 2.0 LineEdit.qml
|
||||
OriginControl 2.0 OriginControl.qml
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include <nodemetainfo.h>
|
||||
#include <qmlobjectnode.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
//using namespace QmlDesigner;
|
||||
|
||||
@@ -348,6 +349,99 @@ QString PropertyEditorValue::getTranslationContext() const
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool PropertyEditorValue::isIdList() const
|
||||
{
|
||||
if (modelNode().isValid() && modelNode().metaInfo().isValid() && modelNode().metaInfo().hasProperty(name())) {
|
||||
const QmlDesigner::QmlObjectNode objectNode(modelNode());
|
||||
if (objectNode.isValid() && objectNode.hasBindingProperty(name())) {
|
||||
static const QRegExp rx("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
|
||||
const QString exp = objectNode.propertyAffectedByCurrentState(name()) ? expression() : modelNode().bindingProperty(name()).expression();
|
||||
for (const auto &str : generateStringList(exp))
|
||||
{
|
||||
if (!rx.exactMatch(str))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList PropertyEditorValue::getExpressionAsList() const
|
||||
{
|
||||
return generateStringList(expression());
|
||||
}
|
||||
|
||||
bool PropertyEditorValue::idListAdd(const QString &value)
|
||||
{
|
||||
QTC_ASSERT(isIdList(), return false);
|
||||
|
||||
static const QRegExp rx("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
|
||||
if (!rx.exactMatch(value))
|
||||
return false;
|
||||
|
||||
auto stringList = generateStringList(expression());
|
||||
stringList.append(value);
|
||||
setExpressionWithEmit(generateString(stringList));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEditorValue::idListRemove(int idx)
|
||||
{
|
||||
QTC_ASSERT(isIdList(), return false);
|
||||
|
||||
auto stringList = generateStringList(expression());
|
||||
|
||||
if (idx < 0 || idx >= stringList.size())
|
||||
return false;
|
||||
|
||||
stringList.removeAt(idx);
|
||||
setExpressionWithEmit(generateString(stringList));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyEditorValue::idListReplace(int idx, const QString &value)
|
||||
{
|
||||
QTC_ASSERT(isIdList(), return false);
|
||||
|
||||
static const QRegExp rx("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
|
||||
if (!rx.exactMatch(value))
|
||||
return false;
|
||||
|
||||
auto stringList = generateStringList(expression());
|
||||
|
||||
if (idx < 0 || idx >= stringList.size())
|
||||
return false;
|
||||
|
||||
stringList.replace(idx, value);
|
||||
setExpressionWithEmit(generateString(stringList));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList PropertyEditorValue::generateStringList(const QString &string) const
|
||||
{
|
||||
QString copy = string;
|
||||
copy = copy.remove("[").remove("]");
|
||||
|
||||
QStringList tmp = copy.split(",", QString::SkipEmptyParts);
|
||||
for (QString &str : tmp)
|
||||
str = str.trimmed();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
QString PropertyEditorValue::generateString(const QStringList &stringList) const
|
||||
{
|
||||
if (stringList.size() > 1)
|
||||
return "[" + stringList.join(",") + "]";
|
||||
else
|
||||
return stringList.first();
|
||||
}
|
||||
|
||||
void PropertyEditorValue::registerDeclarativeTypes()
|
||||
{
|
||||
qmlRegisterType<PropertyEditorValue>("HelperWidgets",2,0,"PropertyEditorValue");
|
||||
@@ -490,4 +584,3 @@ void PropertyEditorNodeWrapper::update()
|
||||
emit typeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -83,6 +83,8 @@ class PropertyEditorValue : public QObject
|
||||
Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged FINAL)
|
||||
Q_PROPERTY(bool isTranslated READ isTranslated NOTIFY expressionChanged FINAL)
|
||||
|
||||
Q_PROPERTY(QStringList expressionAsList READ getExpressionAsList NOTIFY expressionChanged FINAL)
|
||||
|
||||
Q_PROPERTY(QString name READ nameAsQString FINAL)
|
||||
Q_PROPERTY(PropertyEditorNodeWrapper* complexNode READ complexNode NOTIFY complexNodeChanged FINAL)
|
||||
|
||||
@@ -130,6 +132,13 @@ public:
|
||||
|
||||
Q_INVOKABLE QString getTranslationContext() const;
|
||||
|
||||
bool isIdList() const;
|
||||
|
||||
Q_INVOKABLE QStringList getExpressionAsList() const;
|
||||
Q_INVOKABLE bool idListAdd(const QString &value);
|
||||
Q_INVOKABLE bool idListRemove(int idx);
|
||||
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
|
||||
|
||||
public slots:
|
||||
void resetValue();
|
||||
void setEnumeration(const QString &scope, const QString &name);
|
||||
@@ -149,7 +158,10 @@ signals:
|
||||
void isValidChanged();
|
||||
void isExplicitChanged();
|
||||
|
||||
private: //variables
|
||||
private:
|
||||
QStringList generateStringList(const QString &string) const;
|
||||
QString generateString(const QStringList &stringList) const;
|
||||
|
||||
QmlDesigner::ModelNode m_modelNode;
|
||||
QVariant m_value;
|
||||
QString m_expression;
|
||||
|
Reference in New Issue
Block a user