summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenning Gruendl <henning.gruendl@qt.io>2019-12-05 10:53:57 +0100
committerHenning Gründl <henning.gruendl@qt.io>2019-12-06 13:11:30 +0000
commitf4d6300b0b08506efdc34c0d170822de1c4d911b (patch)
tree109d77e13dcb4154aabc200812457e9713ebb71c
parentc6ab1d8b4f43ff3d458667273955e7194ba718b5 (diff)
downloadqt-creator-f4d6300b0b08506efdc34c0d170822de1c4d911b.tar.gz
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>
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml213
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml60
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp95
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h14
5 files changed, 382 insertions, 2 deletions
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml
new file mode 100644
index 0000000000..050f8f45ed
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml
@@ -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)
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml
new file mode 100644
index 0000000000..d1daa7baf8
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml
@@ -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
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
index 11d39ed9e3..3918b26aa1 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
@@ -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
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index c4a928b1ec..8c4bc83297 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -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();
}
}
-
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index e74196f55c..481fbb3458 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -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;