forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
@@ -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(),
|
||||
|
@@ -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";
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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
|
@@ -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
|
Reference in New Issue
Block a user