CMake generator: Make file picker a treeview

Task-number: QDS-5836
Change-Id: Ib261ac8750baae7ce55d9c990b5dadb26fc6ac03
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Tapani Mattila
2021-12-21 20:04:32 +02:00
parent b9798f35e6
commit 72c3d675a0
10 changed files with 345 additions and 189 deletions

View File

@@ -27,11 +27,12 @@ add_qtc_plugin(QmlDesigner
designmodewidget.cpp designmodewidget.h designmodewidget.cpp designmodewidget.h
documentmanager.cpp documentmanager.h documentmanager.cpp documentmanager.h
documentwarningwidget.cpp documentwarningwidget.h documentwarningwidget.cpp documentwarningwidget.h
cmakegeneratordialog.h cmakegeneratordialog.cpp checkablefiletreeitem.cpp checkablefiletreeitem.h
cmakegeneratordialog.cpp cmakegeneratordialog.h
cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h
generateresource.cpp generateresource.h generateresource.cpp generateresource.h
generatecmakelists.cpp generatecmakelists.h generatecmakelists.cpp generatecmakelists.h
generatecmakelistsconstants.h generatecmakelistsconstants.h
checkablefilelistmodel.cpp checkablefilelistmodel.h
openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui
qmldesignerconstants.h qmldesignerconstants.h
qmldesignericons.h qmldesignericons.h

View File

@@ -1,111 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Tooling
**
** 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.
**
****************************************************************************/
#include "checkablefilelistmodel.h"
using namespace Utils;
namespace QmlDesigner {
CheckableFileListModel::CheckableFileListModel(const FilePath &rootDir, const FilePaths &files, bool checkedByDefault, QObject *parent)
:QStandardItemModel(parent),
rootDir(rootDir)
{
for (const FilePath &file: files) {
appendRow(new CheckableStandardItem(file.toString(), checkedByDefault));
}
}
QList<CheckableStandardItem*> CheckableFileListModel::checkedItems() const
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QList<QStandardItem*> allItems = findItems("*", Qt::MatchWildcard);
#else
QList<QStandardItem*> allItems = findItems(".*", Qt::MatchRegularExpression);
#endif
QList<CheckableStandardItem*> checkedItems;
for (QStandardItem *standardItem : allItems) {
CheckableStandardItem *item = static_cast<CheckableStandardItem*>(standardItem);
if (item->isChecked())
checkedItems.append(item);
}
return checkedItems;
}
QVariant CheckableFileListModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
if (role == Qt::CheckStateRole) {
CheckableStandardItem *item = static_cast<CheckableStandardItem*>(QStandardItemModel::item(index.row()));
return item->isChecked() ? Qt::Checked : Qt::Unchecked;
}
else if (role == Qt::DisplayRole) {
QVariant data = QStandardItemModel::data(index, role);
FilePath fullPath = FilePath::fromString(data.toString());
FilePath relativePath = fullPath.relativeChildPath(rootDir);
return QVariant(relativePath.toString());
}
}
return QStandardItemModel::data(index, role);
}
bool CheckableFileListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::CheckStateRole)
{
CheckableStandardItem *item = static_cast<CheckableStandardItem*>(QStandardItemModel::item(index.row()));
item->setChecked(value.value<bool>());
return true;
}
return QStandardItemModel::setData(index, value, role);
}
CheckableStandardItem::CheckableStandardItem(const QString &text, bool checked)
:QStandardItem(text),
checked(checked)
{
setFlags(flags() |= Qt::ItemIsUserCheckable);
}
void CheckableStandardItem::setChecked(bool checked)
{
this->checked = checked;
}
bool CheckableStandardItem::isChecked() const
{
return this->checked;
}
int CheckableStandardItem::type() const
{
return QStandardItem::UserType + 0x74d4f1;
}
} //QmlDesigner

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Tooling
**
** 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.
**
****************************************************************************/
#include "checkablefiletreeitem.h"
using namespace Utils;
namespace QmlDesigner {
CheckableFileTreeItem::CheckableFileTreeItem(const FilePath &filePath)
:QStandardItem(filePath.toString())
{
Qt::ItemFlags itemFlags = flags();
if (isFile())
itemFlags |= Qt::ItemIsUserCheckable;
itemFlags &= ~(Qt::ItemIsEditable | Qt::ItemIsSelectable);
setFlags(itemFlags);
}
const FilePath CheckableFileTreeItem::toFilePath() const
{
return FilePath::fromString(text());
}
bool CheckableFileTreeItem::isFile() const
{
return FilePath::fromString(text()).isFile();
}
bool CheckableFileTreeItem::isDir() const
{
return FilePath::fromString(text()).isDir();
}
void CheckableFileTreeItem::setChecked(bool checked)
{
this->checked = checked;
}
bool CheckableFileTreeItem::isChecked() const
{
return this->checked;
}
} //QmlDesigner

View File

@@ -23,43 +23,31 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef CHECKABLEFILELISTMODEL_H #ifndef CHECKABLEFILETREEITEM_H
#define CHECKABLEFILELISTMODEL_H #define CHECKABLEFILETREEITEM_H
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <QStandardItemModel>
#include <QStandardItem>
namespace QmlDesigner { namespace QmlDesigner {
class CheckableStandardItem : public QStandardItem class CheckableFileTreeItem : public QStandardItem
{ {
public: public:
explicit CheckableStandardItem(const QString &text = QString(), bool checked = false); explicit CheckableFileTreeItem(const Utils::FilePath &text = Utils::FilePath());
const Utils::FilePath toFilePath() const;
bool isFile() const;
bool isDir() const;
bool isChecked() const; bool isChecked() const;
void setChecked(bool checked); void setChecked(bool checked);
int type() const;
private: private:
bool checked; bool checked;
}; };
class CheckableFileListModel : public QStandardItemModel
{
public:
CheckableFileListModel(const Utils::FilePath &rootDir,
const Utils::FilePaths &files,
bool checkedByDefault = false,
QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QList<CheckableStandardItem*> checkedItems() const;
protected:
Utils::FilePath rootDir;
};
} //QmlDesigner } //QmlDesigner
Q_DECLARE_METATYPE(QmlDesigner::CheckableStandardItem) #endif // CHECKABLEFILETREEITEM_H
#endif // CHECKABLEFILELISTMODEL_H

View File

@@ -24,13 +24,14 @@
****************************************************************************/ ****************************************************************************/
#include "cmakegeneratordialog.h" #include "cmakegeneratordialog.h"
#include "cmakegeneratordialogtreemodel.h"
#include "generatecmakelistsconstants.h" #include "generatecmakelistsconstants.h"
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QPushButton> #include <QPushButton>
#include <QLayout> #include <QLayout>
#include <QLabel> #include <QLabel>
#include <QListView> #include <QTreeView>
using namespace Utils; using namespace Utils;
@@ -51,12 +52,15 @@ CmakeGeneratorDialog::CmakeGeneratorDialog(const FilePath &rootDir, const FilePa
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
model = new CMakeGeneratorDialogModel(rootDir, files, this); m_model = new CMakeGeneratorDialogTreeModel(rootDir, files, this);
QListView *list = new QListView(this); QTreeView *tree = new QTreeView(this);
list->setModel(model); tree->setModel(m_model);
tree->expandAll();
tree->setHeaderHidden(true);
tree->setItemsExpandable(false);
layout->addWidget(list); layout->addWidget(tree);
layout->addWidget(buttons); layout->addWidget(buttons);
} }
@@ -64,44 +68,13 @@ FilePaths CmakeGeneratorDialog::getFilePaths()
{ {
FilePaths paths; FilePaths paths;
QList<CheckableStandardItem*> items = model->checkedItems(); QList<CheckableFileTreeItem*> items = m_model->checkedItems();
for (CheckableStandardItem *item: items) { for (CheckableFileTreeItem *item: items) {
paths.append(FilePath::fromString(item->text())); paths.append(FilePath::fromString(item->text()));
} }
return paths; return paths;
} }
CMakeGeneratorDialogModel::CMakeGeneratorDialogModel(const Utils::FilePath &rootDir, const Utils::FilePaths &files, QObject *parent)
:CheckableFileListModel(rootDir, files, parent)
{
for (int i=0; i<rowCount(); i++) {
CheckableStandardItem *item = static_cast<CheckableStandardItem*>(QStandardItemModel::item(i));
item->setChecked(CMakeGeneratorDialogModel::checkedByDefault(FilePath::fromString(item->text())));
}
}
bool CMakeGeneratorDialogModel::checkedByDefault(const FilePath &path) const
{
if (path.exists()) {
QString relativePath = path.relativeChildPath(rootDir).toString();
if (relativePath.compare(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS) == 0)
return false;
if (relativePath.endsWith(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS)
&& relativePath.length() > QString(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS).length())
return true;
if (relativePath.compare(QmlDesigner::GenerateCmake::Constants::FILENAME_MODULES) == 0)
return true;
if (relativePath.compare(
FilePath::fromString(QmlDesigner::GenerateCmake::Constants::DIRNAME_CPP)
.pathAppended(QmlDesigner::GenerateCmake::Constants::FILENAME_MAINCPP_HEADER)
.toString())
== 0)
return true;
}
return !path.exists();
}
} }
} }

View File

@@ -27,7 +27,7 @@
#ifndef CMAKEGENERATORDIALOG_H #ifndef CMAKEGENERATORDIALOG_H
#define CMAKEGENERATORDIALOG_H #define CMAKEGENERATORDIALOG_H
#include "checkablefilelistmodel.h" #include "cmakegeneratordialogtreemodel.h"
#include <utils/fileutils.h> #include <utils/fileutils.h>
@@ -37,14 +37,6 @@
namespace QmlDesigner { namespace QmlDesigner {
namespace GenerateCmake { namespace GenerateCmake {
class CMakeGeneratorDialogModel : public CheckableFileListModel
{
public:
CMakeGeneratorDialogModel(const Utils::FilePath &rootDir, const Utils::FilePaths &files, QObject *parent = nullptr);
protected:
virtual bool checkedByDefault(const Utils::FilePath &file) const;
};
class CmakeGeneratorDialog : public QDialog class CmakeGeneratorDialog : public QDialog
{ {
public: public:
@@ -52,7 +44,7 @@ public:
Utils::FilePaths getFilePaths(); Utils::FilePaths getFilePaths();
private: private:
CheckableFileListModel *model; CMakeGeneratorDialogTreeModel *m_model;
}; };
} }

View File

@@ -0,0 +1,175 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Tooling
**
** 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.
**
****************************************************************************/
#include "cmakegeneratordialogtreemodel.h"
#include "generatecmakelistsconstants.h"
#include "checkablefiletreeitem.h"
using namespace Utils;
namespace QmlDesigner {
namespace GenerateCmake {
CMakeGeneratorDialogTreeModel::CMakeGeneratorDialogTreeModel(const FilePath &rootDir,
const FilePaths &files, QObject *parent)
:QStandardItemModel(parent),
rootDir(rootDir),
m_icons(new QFileIconProvider())
{
createNodes(files, invisibleRootItem());
}
CMakeGeneratorDialogTreeModel::~CMakeGeneratorDialogTreeModel()
{
delete m_icons;
}
QVariant CMakeGeneratorDialogTreeModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
const CheckableFileTreeItem *node = constNodeForIndex(index);
if (role == Qt::CheckStateRole) {
if (node->isFile())
return node->isChecked() ? Qt::Checked : Qt::Unchecked;
return {};
}
else if (role == Qt::DisplayRole) {
FilePath fullPath = node->toFilePath();
return QVariant(fullPath.fileName());
}
else if (role == Qt::DecorationRole) {
if (!node->isFile())
return m_icons->icon(QFileIconProvider::Folder);
}
}
return QStandardItemModel::data(index, role);
}
bool CMakeGeneratorDialogTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid()) {
CheckableFileTreeItem *node = nodeForIndex(index);
if (role == Qt::CheckStateRole) {
node->setChecked(value.value<bool>());
return true;
}
}
return QStandardItemModel::setData(index, value, role);;
}
const QList<CheckableFileTreeItem*> CMakeGeneratorDialogTreeModel::checkedItems() const
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QList<QStandardItem*> allItems = findItems("*", Qt::MatchWildcard);
#else
QList<QStandardItem*> allItems = findItems(".*", Qt::MatchRegularExpression);
#endif
QList<CheckableFileTreeItem*> checkedItems;
for (QStandardItem *standardItem : allItems) {
CheckableFileTreeItem *item = static_cast<CheckableFileTreeItem*>(standardItem);
if (item->isChecked())
checkedItems.append(item);
}
return checkedItems;
}
bool CMakeGeneratorDialogTreeModel::checkedByDefault(const Utils::FilePath &file) const
{
if (file.exists()) {
QString relativePath = file.relativeChildPath(rootDir).toString();
if (relativePath.compare(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS) == 0)
return false;
if (relativePath.endsWith(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS)
&& relativePath.length() > QString(QmlDesigner::GenerateCmake::Constants::FILENAME_CMAKELISTS).length())
return true;
if (relativePath.compare(QmlDesigner::GenerateCmake::Constants::FILENAME_MODULES) == 0)
return true;
if (relativePath.compare(
FilePath::fromString(QmlDesigner::GenerateCmake::Constants::DIRNAME_CPP)
.pathAppended(QmlDesigner::GenerateCmake::Constants::FILENAME_MAINCPP_HEADER)
.toString())
== 0)
return true;
}
return !file.exists();
}
void CMakeGeneratorDialogTreeModel::createNodes(const FilePaths &candidates, QStandardItem *parent)
{
if (!parent)
return;
CheckableFileTreeItem *checkParent = dynamic_cast<CheckableFileTreeItem*>(parent);
FilePath thisDir = (parent == invisibleRootItem()) ? rootDir : checkParent->toFilePath();
for (const FilePath &file : candidates) {
if (file.parentDir() == thisDir) {
CheckableFileTreeItem *fileNode = new CheckableFileTreeItem(file);
fileNode->setChecked(checkedByDefault(file));
parent->appendRow(fileNode);
}
}
FilePaths directSubDirs;
for (const FilePath &file : candidates) {
FilePath dir = file.parentDir();
if (dir.parentDir() == thisDir && !directSubDirs.contains(dir))
directSubDirs.append(dir);
}
for (const FilePath &subDir : directSubDirs) {
CheckableFileTreeItem *dirNode = new CheckableFileTreeItem(subDir);
parent->appendRow(dirNode);
FilePaths subDirCandidates;
for (const FilePath &file : candidates)
if (file.isChildOf(subDir))
subDirCandidates.append(file);
createNodes(subDirCandidates, dirNode);
}
}
const CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::constNodeForIndex(const QModelIndex &index) const
{
const QStandardItem *parent = static_cast<const QStandardItem*>(index.constInternalPointer());
const QStandardItem *item = parent->child(index.row(), index.column());
return static_cast<const CheckableFileTreeItem*>(item);
}
CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::nodeForIndex(const QModelIndex &index)
{
QStandardItem *parent = static_cast<QStandardItem*>(index.internalPointer());
QStandardItem *item = parent->child(index.row(), index.column());
return static_cast<CheckableFileTreeItem*>(item);
}
} //GenerateCmake
} //QmlDesigner

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Tooling
**
** 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.
**
****************************************************************************/
#ifndef CMAKEGENERATORDIALOGTREEMODEL_H
#define CMAKEGENERATORDIALOGTREEMODEL_H
#include "checkablefiletreeitem.h"
#include <QFileIconProvider>
#include <QStandardItemModel>
#include <utils/fileutils.h>
namespace QmlDesigner {
namespace GenerateCmake {
class CMakeGeneratorDialogTreeModel : public QStandardItemModel
{
public:
CMakeGeneratorDialogTreeModel(const Utils::FilePath &rootDir,
const Utils::FilePaths &files, QObject *parent = nullptr);
~CMakeGeneratorDialogTreeModel();
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
const QList<CheckableFileTreeItem*> checkedItems() const;
const CheckableFileTreeItem* constNodeForIndex(const QModelIndex &index) const;
CheckableFileTreeItem* nodeForIndex(const QModelIndex &index);
protected:
bool checkedByDefault(const Utils::FilePath &file) const;
Utils::FilePath rootDir;
private:
void createNodes(const Utils::FilePaths &candidates, QStandardItem *parent);
QFileIconProvider* m_icons;
};
} //GenerateCmake
} //QmlDesigner
#endif // CMAKEGENERATORDIALOGTREEMODEL_H

View File

@@ -8,8 +8,9 @@ HEADERS += $$PWD/qmldesignerconstants.h \
$$PWD/generateresource.h \ $$PWD/generateresource.h \
$$PWD/generatecmakelists.h \ $$PWD/generatecmakelists.h \
$$PWD/generatecmakelistsconstants.h \ $$PWD/generatecmakelistsconstants.h \
$$PWD/checkablefilelistmodel.h \ $$PWD/checkablefiletreeitem.h \
$$PWD/cmakegeneratordialog.h \ $$PWD/cmakegeneratordialog.h \
$$PWD/cmakegeneratordialogtreemodel.h \
$$PWD/settingspage.h \ $$PWD/settingspage.h \
$$PWD/designmodecontext.h \ $$PWD/designmodecontext.h \
$$PWD/documentmanager.h \ $$PWD/documentmanager.h \
@@ -27,8 +28,9 @@ SOURCES += $$PWD/qmldesignerplugin.cpp \
$$PWD/editorproxy.cpp \ $$PWD/editorproxy.cpp \
$$PWD/generateresource.cpp \ $$PWD/generateresource.cpp \
$$PWD/generatecmakelists.cpp \ $$PWD/generatecmakelists.cpp \
$$PWD/checkablefilelistmodel.cpp \ $$PWD/checkablefiletreeitem.cpp \
$$PWD/cmakegeneratordialog.cpp \ $$PWD/cmakegeneratordialog.cpp \
$$PWD/cmakegeneratordialogtreemodel.cpp \
$$PWD/settingspage.cpp \ $$PWD/settingspage.cpp \
$$PWD/designmodecontext.cpp \ $$PWD/designmodecontext.cpp \
$$PWD/documentmanager.cpp \ $$PWD/documentmanager.cpp \

View File

@@ -1018,8 +1018,10 @@ Project {
"generatecmakelists.cpp", "generatecmakelists.cpp",
"generatecmakelists.h", "generatecmakelists.h",
"generatecmakelistsconstants.h", "generatecmakelistsconstants.h",
"checkablefilelistmodel.cpp", "checkablefiletreeitem.cpp",
"checkablefilelistmodel.h", "checkablefiletreeitem.h",
"cmakegeneratordialogtreemodel.cpp",
"cmakegeneratordialogtreemodel.h",
"cmakegeneratordialog.cpp", "cmakegeneratordialog.cpp",
"cmakegeneratordialog.h", "cmakegeneratordialog.h",
"designersettings.cpp", "designersettings.cpp",