diff options
author | Henning Gruendl <henning.gruendl@qt.io> | 2019-12-05 10:53:57 +0100 |
---|---|---|
committer | Henning Gründl <henning.gruendl@qt.io> | 2019-12-06 13:11:30 +0000 |
commit | f4d6300b0b08506efdc34c0d170822de1c4d911b (patch) | |
tree | 109d77e13dcb4154aabc200812457e9713ebb71c | |
parent | c6ab1d8b4f43ff3d458667273955e7194ba718b5 (diff) | |
download | qt-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>
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; |