QmlDesigner: Add a view for single collection

Change-Id: Iee103cf9344872e0f2eaa564fa1feeaea4d26d6a
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2023-09-08 15:14:42 +03:00
parent f12f3790da
commit 252a1f3c74
10 changed files with 326 additions and 7 deletions

View File

@@ -14,6 +14,7 @@ Item {
property var rootView: CollectionEditorBackend.rootView
property var model: CollectionEditorBackend.model
property var singleCollectionModel: CollectionEditorBackend.singleCollectionModel
function showWarning(title, message) {
warningDialog.title = title
@@ -60,9 +61,9 @@ Item {
width: parent.width
Rectangle {
width: parent.width
height: StudioTheme.Values.height + 5
color: StudioTheme.Values.themeToolbarBackground
width: parent.width
Text {
id: collectionText
@@ -141,11 +142,8 @@ Item {
}
}
Rectangle {
id: collectionRect
color: StudioTheme.Values.themeBackgroundColorAlternate
SingleCollectionView {
model: root.singleCollectionModel
anchors {
left: collectionsRect.right
right: parent.right

View File

@@ -0,0 +1,134 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import StudioTheme 1.0 as StudioTheme
Rectangle {
id: root
required property var model
property alias leftPadding: topRow.leftPadding
property real rightPadding: topRow.rightPadding
color: StudioTheme.Values.themeBackgroundColorAlternate
Column {
id: topRow
spacing: 0
width: parent.width
leftPadding: 20
rightPadding: 0
topPadding: 5
Text {
id: collectionNameText
leftPadding: 8
rightPadding: 8
topPadding: 3
bottomPadding: 3
color: StudioTheme.Values.themeTextColor
text: root.model.collectionName
font.pixelSize: StudioTheme.Values.mediumIconFont
elide: Text.ElideRight
Rectangle {
anchors.fill: parent
z: parent.z - 1
color: StudioTheme.Values.themeBackgroundColorNormal
}
}
Item { // spacer
width: 1
height: 10
}
HorizontalHeaderView {
id: headerView
property real topPadding: 5
property real bottomPadding: 5
height: headerMetrics.height + topPadding + bottomPadding
syncView: tableView
clip: true
delegate: Rectangle {
implicitWidth: 100
implicitHeight: headerText.height
color: StudioTheme.Values.themeControlBackground
border.width: 2
border.color: StudioTheme.Values.themeControlOutline
clip: true
Text {
id: headerText
topPadding: headerView.topPadding
bottomPadding: headerView.bottomPadding
leftPadding: 5
rightPadding: 5
text: display
font.pixelSize: headerMetrics.font
color: StudioTheme.Values.themeIdleGreen
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.centerIn: parent
elide: Text.ElideRight
}
}
TextMetrics {
id: headerMetrics
font.pixelSize: StudioTheme.Values.baseFontSize
text: "Xq"
}
}
}
TableView {
id: tableView
anchors {
top: topRow.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
leftMargin: root.leftPadding
rightMargin: root.rightPadding
}
model: root.model
delegate: Rectangle {
implicitWidth: 100
implicitHeight: itemText.height
color: StudioTheme.Values.themeControlBackground
border.width: 1
border.color: StudioTheme.Values.themeControlOutline
Text {
id: itemText
text: display
width: parent.width
leftPadding: 5
topPadding: 3
bottomPadding: 3
font.pixelSize: StudioTheme.Values.baseFontSize
color: StudioTheme.Values.themeTextColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
}
}

View File

@@ -808,6 +808,7 @@ extend_qtc_plugin(QmlDesigner
collectionmodel.cpp collectionmodel.h
collectionview.cpp collectionview.h
collectionwidget.cpp collectionwidget.h
singlecollectionmodel.cpp singlecollectionmodel.h
)
extend_qtc_plugin(QmlDesigner

View File

@@ -171,6 +171,15 @@ void CollectionModel::selectCollection(const ModelNode &node)
selectCollectionIndex(nodePlace, true);
}
QmlDesigner::ModelNode CollectionModel::collectionNodeAt(int idx)
{
QModelIndex data = index(idx);
if (!data.isValid())
return {};
return m_collections.at(idx);
}
bool CollectionModel::isEmpty() const
{
return m_collections.isEmpty();

View File

@@ -41,6 +41,8 @@ public:
int collectionIndex(const ModelNode &node) const;
void selectCollection(const ModelNode &node);
ModelNode collectionNodeAt(int idx);
Q_INVOKABLE bool isEmpty() const;
Q_INVOKABLE void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
Q_INVOKABLE void deselect();

View File

@@ -9,6 +9,7 @@
#include "nodemetainfo.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "singlecollectionmodel.h"
#include "variantproperty.h"
#include <QJsonArray>
@@ -352,6 +353,12 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data());
Core::ICore::addContextObject(collectionEditorContext);
CollectionModel *collectionModel = m_widget->collectionModel().data();
connect(collectionModel, &CollectionModel::selectedIndexChanged, this, [&](int selectedIndex) {
m_widget->singleCollectionModel()->setCollection(
m_widget->collectionModel()->collectionNodeAt(selectedIndex));
});
}
return createWidgetInfo(m_widget.data(),

View File

@@ -6,6 +6,7 @@
#include "collectionview.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include "singlecollectionmodel.h"
#include "theme.h"
#include <studioquickwidget.h>
@@ -36,6 +37,7 @@ CollectionWidget::CollectionWidget(CollectionView *view)
: QFrame()
, m_view(view)
, m_model(new CollectionModel)
, m_singleCollectionModel(new SingleCollectionModel)
, m_quickWidget(new StudioQuickWidget(this))
{
setWindowTitle(tr("Collection View", "Title of collection view widget"));
@@ -62,7 +64,9 @@ CollectionWidget::CollectionWidget(CollectionView *view)
qmlRegisterAnonymousType<CollectionWidget>("CollectionEditorBackend", 1);
auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend");
map->setProperties(
{{"rootView", QVariant::fromValue(this)}, {"model", QVariant::fromValue(m_model.data())}});
{{"rootView", QVariant::fromValue(this)},
{"model", QVariant::fromValue(m_model.data())},
{"singleCollectionModel", QVariant::fromValue(m_singleCollectionModel.data())}});
auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this);
connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource);
@@ -83,6 +87,11 @@ QPointer<CollectionModel> CollectionWidget::collectionModel() const
return m_model;
}
QPointer<SingleCollectionModel> CollectionWidget::singleCollectionModel() const
{
return m_singleCollectionModel;
}
void CollectionWidget::reloadQmlSource()
{
const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml";

View File

@@ -12,6 +12,7 @@ namespace QmlDesigner {
class CollectionModel;
class CollectionView;
class SingleCollectionModel;
class CollectionWidget : public QFrame
{
@@ -22,6 +23,7 @@ public:
void contextHelp(const Core::IContext::HelpCallback &callback) const;
QPointer<CollectionModel> collectionModel() const;
QPointer<SingleCollectionModel> singleCollectionModel() const;
void reloadQmlSource();
@@ -36,6 +38,7 @@ public:
private:
QPointer<CollectionView> m_view;
QPointer<CollectionModel> m_model;
QPointer<SingleCollectionModel> m_singleCollectionModel;
QScopedPointer<StudioQuickWidget> m_quickWidget;
};

View File

@@ -0,0 +1,110 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "singlecollectionmodel.h"
#include "nodemetainfo.h"
#include "variantproperty.h"
#include <utils/qtcassert.h>
namespace {
inline bool isListElement(const QmlDesigner::ModelNode &node)
{
return node.metaInfo().isQtQuickListElement();
}
inline QByteArrayList getHeaders(const QByteArray &headersValue)
{
QByteArrayList result;
const QByteArrayList initialHeaders = headersValue.split(',');
for (QByteArray header : initialHeaders) {
header = header.trimmed();
if (header.size())
result.append(header);
}
return result;
}
} // namespace
namespace QmlDesigner {
SingleCollectionModel::SingleCollectionModel(QObject *parent)
: QAbstractTableModel(parent)
{}
int SingleCollectionModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
{
return m_elements.count();
}
int SingleCollectionModel::columnCount([[maybe_unused]] const QModelIndex &parent) const
{
return m_headers.count();
}
QVariant SingleCollectionModel::data(const QModelIndex &index, int) const
{
if (!index.isValid())
return {};
const QByteArray &propertyName = m_headers.at(index.column());
const ModelNode &elementNode = m_elements.at(index.row());
if (elementNode.hasVariantProperty(propertyName))
return elementNode.variantProperty(propertyName).value();
return {};
}
bool SingleCollectionModel::setData(const QModelIndex &, const QVariant &, int)
{
return false;
}
Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return {};
return {Qt::ItemIsSelectable | Qt::ItemIsEnabled};
}
QVariant SingleCollectionModel::headerData(int section,
Qt::Orientation orientation,
[[maybe_unused]] int role) const
{
if (orientation == Qt::Horizontal)
return m_headers.at(section);
return {};
}
void SingleCollectionModel::setCollection(const ModelNode &collection)
{
beginResetModel();
m_collectionNode = collection;
updateCollectionName();
QTC_ASSERT(collection.isValid() && collection.hasVariantProperty("headers"), {
m_headers.clear();
m_elements.clear();
endResetModel();
return;
});
m_headers = getHeaders(collection.variantProperty("headers").value().toByteArray());
m_elements = Utils::filtered(collection.allSubModelNodes(), &isListElement);
endResetModel();
}
void SingleCollectionModel::updateCollectionName()
{
QString newCollectionName = m_collectionNode.isValid()
? m_collectionNode.variantProperty("objectName").value().toString()
: "";
if (m_collectionName != newCollectionName) {
m_collectionName = newCollectionName;
emit this->collectionNameChanged(m_collectionName);
}
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "modelnode.h"
#include <QAbstractTableModel>
QT_BEGIN_NAMESPACE
class QJsonArray;
QT_END_NAMESPACE
namespace QmlDesigner {
class SingleCollectionModel : public QAbstractTableModel
{
Q_OBJECT
Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged)
public:
explicit SingleCollectionModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section,
Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
void setCollection(const ModelNode &collection);
signals:
void collectionNameChanged(const QString &collectionName);
private:
void updateCollectionName();
QByteArrayList m_headers;
ModelNodes m_elements;
ModelNode m_collectionNode;
QString m_collectionName;
};
} // namespace QmlDesigner