Add components library expand/collapse all context menu options

also change import name from "library" to "module" and relevant tweaks.

Task-number: QDS-3589
Change-Id: Ib467dda61b6720cebe843e34cb807aee6221f5cb
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Mahmoud Badri
2021-02-13 01:27:20 +02:00
parent c26b31b7f7
commit 43eaa09b11
13 changed files with 166 additions and 65 deletions

View File

@@ -33,9 +33,11 @@ import StudioTheme 1.0 as StudioTheme
/* The view displaying the item grid.
The following Qml context properties have to be set:
- ItemLibraryModel listmodel
- int itemLibraryIconWidth
- int itemLibraryIconHeight
- ItemLibraryModel itemLibraryModel
- int itemLibraryIconWidth
- int itemLibraryIconHeight
- ItemLibraryWidget rootView
- QColor highlightColor
itemLibraryModel structure:
@@ -100,11 +102,22 @@ ScrollView {
id: contextMenu
StudioControls.MenuItem {
text: qsTr("Remove Library")
enabled: importToRemove !== ""
&& importToRemove !== "QtQuick"
text: qsTr("Remove Module")
enabled: importToRemove !== "" && importToRemove !== "QtQuick"
onTriggered: rootView.removeImport(importToRemove)
}
StudioControls.MenuSeparator {}
StudioControls.MenuItem {
text: qsTr("Expand All")
onTriggered: itemLibraryModel.expandAll()
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
onTriggered: itemLibraryModel.collapseAll()
}
}
}
@@ -125,7 +138,7 @@ ScrollView {
topPadding: 0
bottomPadding: 0
expanded: importExpanded
onExpandedChanged: itemLibraryModel.setExpanded(expanded, importUrl);
onToggleExpand: importExpanded = !importExpanded
onShowContextMenu: {
importToRemove = importUsed ? "" : importUrl
contextMenu.popup()
@@ -148,7 +161,7 @@ ScrollView {
caption: categoryName + " (" + itemModel.rowCount() + ")"
visible: categoryVisible
expanded: categoryExpanded
onExpandedChanged: itemLibraryModel.setExpanded(expanded, categoryName);
onToggleExpand: categoryExpanded = !categoryExpanded
Grid {
id: itemGrid
@@ -171,5 +184,4 @@ ScrollView {
}
}
}
}

View File

@@ -43,8 +43,6 @@ Item {
property int rightPadding: 0
property int bottomPadding: 4
property int animationDuration: 0
property bool expanded: true
property int level: 0
property int levelShift: 10
@@ -59,6 +57,7 @@ Item {
clip: true
signal showContextMenu()
signal toggleExpand()
Rectangle {
id: header
@@ -75,12 +74,6 @@ Item {
anchors.left: parent.left
anchors.leftMargin: 4 + (level * levelShift)
anchors.verticalCenter: parent.verticalCenter
Behavior on rotation {
NumberAnimation {
easing.type: Easing.OutCubic
duration: section.animationDuration
}
}
}
Controls.Label {
@@ -98,8 +91,8 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
section.animationDuration = 120
section.expanded = !section.expanded
trans.enabled = true
section.toggleExpand()
} else {
section.showContextMenu()
}
@@ -135,13 +128,6 @@ Item {
anchors.topMargin: section.topPadding
}
Behavior on implicitHeight {
NumberAnimation {
easing.type: Easing.OutCubic
duration: section.animationDuration
}
}
states: [
State {
name: "Collapsed"
@@ -156,4 +142,14 @@ Item {
}
}
]
transitions: Transition {
id: trans
enabled: false
NumberAnimation {
properties: "implicitHeight,rotation";
duration: 120;
easing.type: Easing.OutCubic
}
}
}

View File

@@ -25,6 +25,7 @@
#include "itemlibrarycategoriesmodel.h"
#include "itemlibrarycategory.h"
#include "itemlibrarymodel.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -72,6 +73,24 @@ QVariant ItemLibraryCategoriesModel::data(const QModelIndex &index, int role) co
return {};
}
bool ItemLibraryCategoriesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// currently only categoryExpanded property is updatable
if (index.isValid() && m_roleNames.contains(role)) {
QVariant currValue = m_categoryList.at(index.row())->property(m_roleNames.value(role));
if (currValue != value) {
m_categoryList[index.row()]->setProperty(m_roleNames.value(role), value);
if (m_roleNames.value(role) == "categoryExpanded") {
ItemLibraryModel::saveExpandedState(value.toBool(),
m_categoryList[index.row()]->categoryName());
}
emit dataChanged(index, index, {role});
return true;
}
}
return false;
}
QHash<int, QByteArray> ItemLibraryCategoriesModel::roleNames() const
{
return m_roleNames;

View File

@@ -44,6 +44,7 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QHash<int, QByteArray> roleNames() const override;
void addCategory(ItemLibraryCategory *category);

View File

@@ -37,7 +37,7 @@ class ItemLibraryCategory : public QObject
Q_PROPERTY(QString categoryName READ categoryName FINAL)
Q_PROPERTY(bool categoryVisible READ isVisible NOTIFY visibilityChanged FINAL)
Q_PROPERTY(bool categoryExpanded READ categoryExpanded FINAL)
Q_PROPERTY(bool categoryExpanded READ categoryExpanded WRITE setExpanded NOTIFY expandedChanged FINAL)
Q_PROPERTY(QObject *itemModel READ itemModel NOTIFY itemModelChanged FINAL)
public:
@@ -62,6 +62,7 @@ public:
signals:
void itemModelChanged();
void visibilityChanged();
void expandedChanged();
private:
ItemLibraryItemsModel m_itemModel;

View File

@@ -28,15 +28,16 @@
namespace QmlDesigner {
ItemLibraryImport::ItemLibraryImport(const Import &import, QObject *parent)
ItemLibraryImport::ItemLibraryImport(const Import &import, QObject *parent, bool isUserSection)
: QObject(parent),
m_import(import)
m_import(import),
m_isUserSection(isUserSection)
{
}
QString ItemLibraryImport::importName() const
{
if (importUrl().isEmpty())
if (m_isUserSection)
return userComponentsTitle();
if (importUrl() == "QtQuick")
@@ -47,6 +48,9 @@ QString ItemLibraryImport::importName() const
QString ItemLibraryImport::importUrl() const
{
if (m_isUserSection)
return userComponentsTitle();
return m_import.url();
}
@@ -57,7 +61,7 @@ bool ItemLibraryImport::importExpanded() const
QString ItemLibraryImport::sortingName() const
{
if (importName() == userComponentsTitle()) // user components always come first
if (m_isUserSection) // user components always come first
return "_";
return importName();
@@ -109,7 +113,7 @@ bool ItemLibraryImport::setVisible(bool isVisible)
return false;
}
bool ItemLibraryImport::isVisible() const
bool ItemLibraryImport::importVisible() const
{
return m_isVisible;
}
@@ -119,17 +123,22 @@ void ItemLibraryImport::setImportUsed(bool importUsed)
m_importUsed = importUsed;
}
bool ItemLibraryImport::isImportUsed() const
bool ItemLibraryImport::importUsed() const
{
return m_importUsed;
}
bool ItemLibraryImport::hasCategories() const
{
return m_categoryModel.rowCount() > 0;
}
void ItemLibraryImport::sortCategorySections()
{
m_categoryModel.sortCategorySections();
}
void ItemLibraryImport::setExpanded(bool expanded)
void ItemLibraryImport::setImportExpanded(bool expanded)
{
m_importExpanded = expanded;
}
@@ -144,6 +153,11 @@ ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &catego
return nullptr;
}
bool ItemLibraryImport::isUserSection() const
{
return m_isUserSection;
}
// static
QString ItemLibraryImport::userComponentsTitle()
{

View File

@@ -38,21 +38,22 @@ class ItemLibraryImport : public QObject
Q_PROPERTY(QString importName READ importName FINAL)
Q_PROPERTY(QString importUrl READ importUrl FINAL)
Q_PROPERTY(bool importVisible READ isVisible NOTIFY visibilityChanged FINAL)
Q_PROPERTY(bool importUsed READ isImportUsed NOTIFY importUsedChanged FINAL)
Q_PROPERTY(bool importExpanded READ importExpanded FINAL)
Q_PROPERTY(bool importVisible READ importVisible NOTIFY importVisibleChanged FINAL)
Q_PROPERTY(bool importUsed READ importUsed NOTIFY importUsedChanged FINAL)
Q_PROPERTY(bool importExpanded READ importExpanded WRITE setImportExpanded NOTIFY importExpandChanged FINAL)
Q_PROPERTY(QObject *categoryModel READ categoryModel NOTIFY categoryModelChanged FINAL)
public:
ItemLibraryImport(const Import &import, QObject *parent = nullptr);
ItemLibraryImport(const Import &import, QObject *parent = nullptr, bool isUserSection = false);
QString importName() const;
QString importUrl() const;
bool importExpanded() const;
QString sortingName() const;
Import importEntry() const;
bool isVisible() const;
bool isImportUsed() const;
bool importVisible() const;
bool importUsed() const;
bool hasCategories() const;
ItemLibraryCategory *getCategorySection(const QString &categoryName) const;
void addCategory(ItemLibraryCategory *category);
@@ -61,20 +62,24 @@ public:
bool setVisible(bool isVisible);
void setImportUsed(bool importUsed);
void sortCategorySections();
void setExpanded(bool expanded);
void setImportExpanded(bool expanded = true);
static QString userComponentsTitle();
bool isUserSection() const;
signals:
void categoryModelChanged();
void visibilityChanged();
void importVisibleChanged();
void importUsedChanged();
void importExpandChanged();
private:
Import m_import;
bool m_importExpanded = true;
bool m_isVisible = true;
bool m_importUsed = false;
bool m_isUserSection = false; // user components import section
ItemLibraryCategoriesModel m_categoryModel;
};

View File

@@ -47,15 +47,48 @@
namespace QmlDesigner {
// sectionName can be an import or category section
void ItemLibraryModel::setExpanded(bool expanded, const QString &sectionName)
// sectionName can be an import url or a category name
void ItemLibraryModel::saveExpandedState(bool expanded, const QString &sectionName)
{
collapsedStateHash.insert(sectionName, expanded);
expandedStateHash.insert(sectionName, expanded);
}
bool ItemLibraryModel::sectionExpanded(const QString &sectionName) const
bool ItemLibraryModel::loadExpandedState(const QString &sectionName)
{
return collapsedStateHash.value(sectionName, true);
return expandedStateHash.value(sectionName, true);
}
void ItemLibraryModel::expandAll()
{
bool changed = false;
for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
if (import->hasCategories() && !import->importExpanded()) {
changed = true;
import->setImportExpanded();
saveExpandedState(true, import->importUrl());
}
}
if (changed) {
beginResetModel();
endResetModel();
}
}
void ItemLibraryModel::collapseAll()
{
bool changed = false;
for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
if (import->hasCategories() && import->importExpanded()) {
changed = true;
import->setImportExpanded(false);
saveExpandedState(false, import->importUrl());
}
}
if (changed) {
beginResetModel();
endResetModel();
}
}
void ItemLibraryModel::setFlowMode(bool b)
@@ -83,7 +116,7 @@ int ItemLibraryModel::rowCount(const QModelIndex & /*parent*/) const
QVariant ItemLibraryModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() +1 > m_importList.count())
if (!index.isValid() || index.row() >= m_importList.count())
return {};
if (m_roleNames.contains(role)) {
@@ -101,6 +134,22 @@ QVariant ItemLibraryModel::data(const QModelIndex &index, int role) const
return {};
}
bool ItemLibraryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// currently only importExpanded property is updatable
if (index.isValid() && m_roleNames.contains(role)) {
QVariant currValue = m_importList.at(index.row())->property(m_roleNames.value(role));
if (currValue != value) {
m_importList[index.row()]->setProperty(m_roleNames.value(role), value);
if (m_roleNames.value(role) == "importExpanded")
saveExpandedState(value.toBool(), m_importList[index.row()]->importUrl());
emit dataChanged(index, index, {role});
return true;
}
}
return false;
}
QHash<int, QByteArray> ItemLibraryModel::roleNames() const
{
return m_roleNames;
@@ -146,7 +195,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
if (import.isLibraryImport()) {
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this);
m_importList.append(itemLibImport);
itemLibImport->setExpanded(sectionExpanded(import.url()));
itemLibImport->setImportExpanded(loadExpandedState(import.url()));
}
}
@@ -184,9 +233,9 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
// create an import section for user components
importSection = importByUrl(ItemLibraryImport::userComponentsTitle());
if (!importSection) {
importSection = new ItemLibraryImport({}, this);
importSection = new ItemLibraryImport({}, this, true);
m_importList.append(importSection);
importSection->setExpanded(sectionExpanded(catName));
importSection->setImportExpanded(loadExpandedState(catName));
}
} else {
if (catName.startsWith("Qt Quick - "))
@@ -205,7 +254,8 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
if (!categorySection) {
categorySection = new ItemLibraryCategory(catName, importSection);
importSection->addCategory(categorySection);
categorySection->setExpanded(sectionExpanded(categorySection->categoryName()));
if (!importSection->isUserSection())
categorySection->setExpanded(loadExpandedState(categorySection->categoryName()));
}
// create item
@@ -251,7 +301,7 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const
if (itemLibraryImport->importUrl() == importUrl
|| (importUrl.isEmpty() && itemLibraryImport->importUrl() == "QtQuick")
|| (importUrl == ItemLibraryImport::userComponentsTitle()
&& itemLibraryImport->importName() == ItemLibraryImport::userComponentsTitle())) {
&& itemLibraryImport->isUserSection())) {
return itemLibraryImport;
}
}

View File

@@ -48,6 +48,7 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QHash<int, QByteArray> roleNames() const override;
QString searchText() const;
@@ -62,15 +63,17 @@ public:
void setFlowMode(bool);
static void registerQmlTypes();
static void saveExpandedState(bool expanded, const QString &sectionName);
static bool loadExpandedState(const QString &sectionName);
Q_INVOKABLE void setExpanded(bool, const QString &section);
Q_INVOKABLE void expandAll();
Q_INVOKABLE void collapseAll();
private:
void updateVisibility(bool *changed);
void addRoleNames();
void sortSections();
void clearSections();
bool sectionExpanded(const QString &sectionName) const;
QList<QPointer<ItemLibraryImport>> m_importList;
QHash<int, QByteArray> m_roleNames;
@@ -78,7 +81,7 @@ private:
QString m_searchText;
bool m_flowMode = false;
QHash<QString, bool> collapsedStateHash;
inline static QHash<QString, bool> expandedStateHash;
};
} // namespace QmlDesigner

View File

@@ -117,8 +117,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
SLOT(handleTabChanged(int)));
QObject::connect(m_headerWidget->rootObject(), SIGNAL(filterChanged(QString)), this,
SLOT(handleFilterChanged(QString)));
QObject::connect(m_headerWidget->rootObject(), SIGNAL(addLibraryClicked()), this,
SLOT(handleAddLibrary()));
QObject::connect(m_headerWidget->rootObject(), SIGNAL(addModuleClicked()), this,
SLOT(handleAddModule()));
QObject::connect(m_headerWidget->rootObject(), SIGNAL(addAssetClicked()), this,
SLOT(handleAddAsset()));
@@ -245,7 +245,7 @@ void ItemLibraryWidget::handleFilterChanged(const QString &filterText)
updateSearch();
}
void ItemLibraryWidget::handleAddLibrary()
void ItemLibraryWidget::handleAddModule()
{
QMetaObject::invokeMethod(m_headerWidget->rootObject(), "setTab", Q_ARG(QVariant, 0));
handleTabChanged(2);
@@ -267,7 +267,7 @@ void ItemLibraryWidget::handleAddImport(int index)
m_model->changeImports({import}, {});
QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager();
m_stackedWidget->setCurrentIndex(0); // switch to the Components Library after import is added
m_stackedWidget->setCurrentIndex(0); // switch to the Components view after import is added
}
void ItemLibraryWidget::delayedUpdateModel()

View File

@@ -130,7 +130,7 @@ private:
private slots:
void handleTabChanged(int index);
void handleFilterChanged(const QString &filterText);
void handleAddLibrary();
void handleAddModule();
void handleAddAsset();
void handleAddImport(int index);
};

View File

@@ -34,7 +34,7 @@ Column {
Text {
id: header
text: qsTr("Select a Library to add")
text: qsTr("Select a Module to Add")
color: "#ffffff"
font.pixelSize: 16
width: parent.width

View File

@@ -34,7 +34,7 @@ Item {
signal tabChanged(int index)
signal filterChanged(string filterText)
signal addLibraryClicked()
signal addModuleClicked()
signal addAssetClicked()
function setTab(index)
@@ -65,7 +65,7 @@ Item {
}
Repeater {
model: [{title: qsTr("Components"), addToolTip: qsTr("Add Library")},
model: [{title: qsTr("Components"), addToolTip: qsTr("Add Module")},
{title: qsTr("Assets"), addToolTip: qsTr("Add new assets to project.")}]
TabButton {
@@ -105,7 +105,7 @@ Item {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: index == 0 ? addLibraryClicked() : addAssetClicked()
onClicked: index == 0 ? addModuleClicked() : addAssetClicked()
}
}