forked from qt-creator/qt-creator
QmlDesigner: Implement sort for the Collection Details View
Task-number: QDS-10986 Change-Id: Iba10830436d58cecb3bd2dba1afc1ac95779123d Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
@@ -13,6 +13,7 @@ Rectangle {
|
||||
|
||||
required property var model
|
||||
required property var backend
|
||||
required property var sortedModel
|
||||
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
@@ -80,7 +81,7 @@ Rectangle {
|
||||
|
||||
Rectangle {
|
||||
clip: true
|
||||
visible: root.model.isEmpty === false
|
||||
visible: !tableView.model.isEmpty
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
border.width: 2
|
||||
@@ -112,14 +113,14 @@ Rectangle {
|
||||
clip: true
|
||||
|
||||
delegate: HeaderDelegate {
|
||||
selectedItem: root.model.selectedColumn
|
||||
selectedItem: tableView.model.selectedColumn
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: (mouse) => {
|
||||
root.model.selectColumn(index)
|
||||
tableView.model.selectColumn(index)
|
||||
|
||||
if (mouse.button === Qt.RightButton)
|
||||
headerMenu.popIndex(index)
|
||||
@@ -151,6 +152,16 @@ Rectangle {
|
||||
text: qsTr("Delete")
|
||||
onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Sort Ascending")
|
||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.AscendingOrder)
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Sort Descending")
|
||||
onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.DescendingOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,22 +176,21 @@ Rectangle {
|
||||
Layout.alignment: Qt.AlignTop + Qt.AlignLeft
|
||||
|
||||
delegate: HeaderDelegate {
|
||||
selectedItem: root.model.selectedRow
|
||||
selectedItem: tableView.model.selectedRow
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: root.model.selectRow(index)
|
||||
onClicked: tableView.model.selectRow(index)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
|
||||
model: root.model
|
||||
model: root.sortedModel
|
||||
clip: true
|
||||
|
||||
Layout.preferredWidth: tableView.contentWidth
|
||||
|
@@ -15,6 +15,7 @@ Item {
|
||||
property var rootView: CollectionEditorBackend.rootView
|
||||
property var model: CollectionEditorBackend.model
|
||||
property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel
|
||||
property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel
|
||||
|
||||
function showWarning(title, message) {
|
||||
warningDialog.title = title
|
||||
@@ -145,6 +146,7 @@ Item {
|
||||
CollectionDetailsView {
|
||||
model: root.collectionDetailsModel
|
||||
backend: root.model
|
||||
sortedModel: root.collectionDetailsSortFilterModel
|
||||
anchors {
|
||||
left: collectionsRect.right
|
||||
right: parent.right
|
||||
|
@@ -841,7 +841,9 @@ extend_qtc_plugin(QmlDesigner
|
||||
SOURCES
|
||||
collectiondetails.cpp collectiondetails.h
|
||||
collectiondetailsmodel.cpp collectiondetailsmodel.h
|
||||
collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h
|
||||
collectioneditorconstants.h
|
||||
collectioneditorutils.cpp collectioneditorutils.h
|
||||
collectionlistmodel.cpp collectionlistmodel.h
|
||||
collectionsourcemodel.cpp collectionsourcemodel.h
|
||||
collectionview.cpp collectionview.h
|
||||
|
@@ -233,6 +233,11 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta
|
||||
return {};
|
||||
}
|
||||
|
||||
CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const
|
||||
{
|
||||
return m_currentCollection.typeAt(column);
|
||||
}
|
||||
|
||||
int CollectionDetailsModel::selectedColumn() const
|
||||
{
|
||||
return m_selectedColumn;
|
||||
|
@@ -43,6 +43,9 @@ public:
|
||||
QVariant headerData(int section,
|
||||
Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
|
||||
CollectionDetails::DataType propertyDataType(int column) const;
|
||||
|
||||
int selectedColumn() const;
|
||||
int selectedRow() const;
|
||||
Q_INVOKABLE QString propertyName(int column) const;
|
||||
|
@@ -0,0 +1,96 @@
|
||||
// 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 "collectiondetailssortfiltermodel.h"
|
||||
|
||||
#include "collectiondetailsmodel.h"
|
||||
#include "collectioneditorutils.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
connect(this, &CollectionDetailsSortFilterModel::rowsInserted,
|
||||
this, &CollectionDetailsSortFilterModel::updateEmpty);
|
||||
connect(this, &CollectionDetailsSortFilterModel::rowsRemoved,
|
||||
this, &CollectionDetailsSortFilterModel::updateEmpty);
|
||||
connect(this, &CollectionDetailsSortFilterModel::modelReset,
|
||||
this, &CollectionDetailsSortFilterModel::updateEmpty);
|
||||
}
|
||||
|
||||
void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model)
|
||||
{
|
||||
m_source = model;
|
||||
Super::setSourceModel(model);
|
||||
connect(m_source, &CollectionDetailsModel::selectedColumnChanged, this, [this](int sourceColumn) {
|
||||
emit selectedColumnChanged(mapFromSource(m_source->index(0, sourceColumn)).column());
|
||||
});
|
||||
|
||||
connect(m_source, &CollectionDetailsModel::selectedRowChanged, this, [this](int sourceRow) {
|
||||
emit selectedRowChanged(mapFromSource(m_source->index(sourceRow, 0)).row());
|
||||
});
|
||||
}
|
||||
|
||||
int CollectionDetailsSortFilterModel::selectedRow() const
|
||||
{
|
||||
QTC_ASSERT(m_source, return -1);
|
||||
|
||||
return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row();
|
||||
}
|
||||
|
||||
int CollectionDetailsSortFilterModel::selectedColumn() const
|
||||
{
|
||||
QTC_ASSERT(m_source, return -1);
|
||||
|
||||
return mapFromSource(m_source->index(0, m_source->selectedColumn())).column();
|
||||
}
|
||||
|
||||
bool CollectionDetailsSortFilterModel::selectRow(int row)
|
||||
{
|
||||
QTC_ASSERT(m_source, return false);
|
||||
|
||||
return m_source->selectRow(mapToSource(index(row, 0)).row());
|
||||
}
|
||||
|
||||
bool CollectionDetailsSortFilterModel::selectColumn(int column)
|
||||
{
|
||||
QTC_ASSERT(m_source, return false);
|
||||
|
||||
return m_source->selectColumn(mapToSource(index(0, column)).column());
|
||||
}
|
||||
|
||||
CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default;
|
||||
|
||||
bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &sourceParent) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft,
|
||||
const QModelIndex &sourceRight) const
|
||||
{
|
||||
QTC_ASSERT(m_source, return false);
|
||||
|
||||
if (sourceleft.column() == sourceRight.column()) {
|
||||
int column = sourceleft.column();
|
||||
CollectionDetails::DataType columnType = m_source->propertyDataType(column);
|
||||
return CollectionEditor::variantIslessThan(sourceleft.data(), sourceRight.data(), columnType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollectionDetailsSortFilterModel::updateEmpty()
|
||||
{
|
||||
bool newValue = rowCount() == 0;
|
||||
if (m_isEmpty != newValue) {
|
||||
m_isEmpty = newValue;
|
||||
emit isEmptyChanged(m_isEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,51 @@
|
||||
// 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 <QSortFilterProxyModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class CollectionDetailsModel;
|
||||
|
||||
class CollectionDetailsSortFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
|
||||
Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
|
||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||
|
||||
using Super = QSortFilterProxyModel;
|
||||
|
||||
public:
|
||||
explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr);
|
||||
virtual ~CollectionDetailsSortFilterModel();
|
||||
|
||||
void setSourceModel(CollectionDetailsModel *model);
|
||||
|
||||
int selectedRow() const;
|
||||
int selectedColumn() const;
|
||||
|
||||
Q_INVOKABLE bool selectRow(int row);
|
||||
Q_INVOKABLE bool selectColumn(int column);
|
||||
|
||||
signals:
|
||||
void selectedColumnChanged(int);
|
||||
void selectedRowChanged(int);
|
||||
void isEmptyChanged(bool);
|
||||
|
||||
protected:
|
||||
using Super::setSourceModel;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override;
|
||||
|
||||
private:
|
||||
void updateEmpty();
|
||||
|
||||
QPointer<CollectionDetailsModel> m_source;
|
||||
bool m_isEmpty = true;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,65 @@
|
||||
// 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 "collectioneditorutils.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <QColor>
|
||||
#include <QUrl>
|
||||
|
||||
namespace {
|
||||
|
||||
using CollectionDataVariant = std::variant<QString, bool, double, QUrl, QColor>;
|
||||
|
||||
inline CollectionDataVariant valueToVariant(const QVariant &value,
|
||||
QmlDesigner::CollectionDetails::DataType type)
|
||||
{
|
||||
using DataType = QmlDesigner::CollectionDetails::DataType;
|
||||
switch (type) {
|
||||
case DataType::String:
|
||||
return value.toString();
|
||||
case DataType::Number:
|
||||
return value.toDouble();
|
||||
case DataType::Boolean:
|
||||
return value.toBool();
|
||||
case DataType::Color:
|
||||
return value.value<QColor>();
|
||||
case DataType::Url:
|
||||
return value.value<QUrl>();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct LessThanVisitor
|
||||
{
|
||||
template<typename T1, typename T2>
|
||||
bool operator()(const T1 &a, const T2 &b) const
|
||||
{
|
||||
return CollectionDataVariant(a).index() < CollectionDataVariant(b).index();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(const T &a, const T &b) const
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool operator()(const QColor &a, const QColor &b) const
|
||||
{
|
||||
return a.name(QColor::HexArgb) < b.name(QColor::HexArgb);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace QmlDesigner::CollectionEditor {
|
||||
|
||||
bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type)
|
||||
{
|
||||
return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner::CollectionEditor
|
@@ -0,0 +1,12 @@
|
||||
// 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 "collectiondetails.h"
|
||||
|
||||
namespace QmlDesigner::CollectionEditor {
|
||||
|
||||
bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
|
||||
|
||||
} // namespace QmlDesigner::CollectionEditor
|
@@ -4,6 +4,7 @@
|
||||
#include "collectionwidget.h"
|
||||
|
||||
#include "collectiondetailsmodel.h"
|
||||
#include "collectiondetailssortfiltermodel.h"
|
||||
#include "collectionsourcemodel.h"
|
||||
#include "collectionview.h"
|
||||
#include "qmldesignerconstants.h"
|
||||
@@ -40,6 +41,7 @@ CollectionWidget::CollectionWidget(CollectionView *view)
|
||||
, m_view(view)
|
||||
, m_sourceModel(new CollectionSourceModel)
|
||||
, m_collectionDetailsModel(new CollectionDetailsModel)
|
||||
, m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>())
|
||||
, m_quickWidget(new StudioQuickWidget(this))
|
||||
{
|
||||
setWindowTitle(tr("Collection View", "Title of collection view widget"));
|
||||
@@ -50,6 +52,8 @@ CollectionWidget::CollectionWidget(CollectionView *view)
|
||||
icontext->setContext(context);
|
||||
icontext->setWidget(this);
|
||||
|
||||
m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel);
|
||||
|
||||
m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR);
|
||||
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports");
|
||||
@@ -65,10 +69,13 @@ CollectionWidget::CollectionWidget(CollectionView *view)
|
||||
|
||||
qmlRegisterAnonymousType<CollectionWidget>("CollectionEditorBackend", 1);
|
||||
auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend");
|
||||
map->setProperties(
|
||||
{{"rootView", QVariant::fromValue(this)},
|
||||
{"model", QVariant::fromValue(m_sourceModel.data())},
|
||||
{"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}});
|
||||
map->setProperties({
|
||||
{"rootView", QVariant::fromValue(this)},
|
||||
{"model", QVariant::fromValue(m_sourceModel.data())},
|
||||
{"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())},
|
||||
{"collectionDetailsSortFilterModel",
|
||||
QVariant::fromValue(m_collectionDetailsSortFilterModel.get())},
|
||||
});
|
||||
|
||||
auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this);
|
||||
connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource);
|
||||
|
@@ -12,6 +12,7 @@ class StudioQuickWidget;
|
||||
namespace QmlDesigner {
|
||||
|
||||
class CollectionDetailsModel;
|
||||
class CollectionDetailsSortFilterModel;
|
||||
class CollectionSourceModel;
|
||||
class CollectionView;
|
||||
|
||||
@@ -40,6 +41,7 @@ private:
|
||||
QPointer<CollectionView> m_view;
|
||||
QPointer<CollectionSourceModel> m_sourceModel;
|
||||
QPointer<CollectionDetailsModel> m_collectionDetailsModel;
|
||||
std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel;
|
||||
QScopedPointer<StudioQuickWidget> m_quickWidget;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user