forked from qt-creator/qt-creator
File System View: Add option to show folders on top
Task-number: QTCREATORBUG-7818 Change-Id: I37a36a03c9baf6fba7b3eedcb52ee5912a57a47a Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -70,6 +70,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
@@ -128,6 +129,10 @@ private:
|
|||||||
class FolderNavigationModel : public QFileSystemModel
|
class FolderNavigationModel : public QFileSystemModel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Roles {
|
||||||
|
IsFolderRole = Qt::UserRole + 50 // leave some gap for the custom roles in QFileSystemModel
|
||||||
|
};
|
||||||
|
|
||||||
explicit FolderNavigationModel(QObject *parent = nullptr);
|
explicit FolderNavigationModel(QObject *parent = nullptr);
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final;
|
||||||
Qt::DropActions supportedDragActions() const final;
|
Qt::DropActions supportedDragActions() const final;
|
||||||
@@ -135,6 +140,37 @@ public:
|
|||||||
bool setData(const QModelIndex &index, const QVariant &value, int role) final;
|
bool setData(const QModelIndex &index, const QVariant &value, int role) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sorts folders on top if wanted
|
||||||
|
class FolderSortProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FolderSortProxyModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
FolderSortProxyModel::FolderSortProxyModel(QObject *parent)
|
||||||
|
: QSortFilterProxyModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderSortProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||||
|
{
|
||||||
|
const QAbstractItemModel *src = sourceModel();
|
||||||
|
if (sortRole() == FolderNavigationModel::IsFolderRole) {
|
||||||
|
const bool leftIsFolder = src->data(source_left, FolderNavigationModel::IsFolderRole)
|
||||||
|
.toBool();
|
||||||
|
const bool rightIsFolder = src->data(source_right, FolderNavigationModel::IsFolderRole)
|
||||||
|
.toBool();
|
||||||
|
if (leftIsFolder != rightIsFolder)
|
||||||
|
return leftIsFolder;
|
||||||
|
}
|
||||||
|
const QString leftName = src->data(source_left, QFileSystemModel::FileNameRole).toString();
|
||||||
|
const QString rightName = src->data(source_right, QFileSystemModel::FileNameRole).toString();
|
||||||
|
return Utils::FileName::fromString(leftName) < Utils::FileName::fromString(rightName);
|
||||||
|
}
|
||||||
|
|
||||||
FolderNavigationModel::FolderNavigationModel(QObject *parent) : QFileSystemModel(parent)
|
FolderNavigationModel::FolderNavigationModel(QObject *parent) : QFileSystemModel(parent)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
@@ -142,6 +178,8 @@ QVariant FolderNavigationModel::data(const QModelIndex &index, int role) const
|
|||||||
{
|
{
|
||||||
if (role == Qt::ToolTipRole)
|
if (role == Qt::ToolTipRole)
|
||||||
return QDir::toNativeSeparators(QDir::cleanPath(filePath(index)));
|
return QDir::toNativeSeparators(QDir::cleanPath(filePath(index)));
|
||||||
|
else if (role == IsFolderRole)
|
||||||
|
return isDir(index);
|
||||||
else
|
else
|
||||||
return QFileSystemModel::data(index, role);
|
return QFileSystemModel::data(index, role);
|
||||||
}
|
}
|
||||||
@@ -250,8 +288,10 @@ static bool isChildOf(const QModelIndex &index, const QModelIndex &parent)
|
|||||||
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent),
|
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent),
|
||||||
m_listView(new Utils::NavigationTreeView(this)),
|
m_listView(new Utils::NavigationTreeView(this)),
|
||||||
m_fileSystemModel(new FolderNavigationModel(this)),
|
m_fileSystemModel(new FolderNavigationModel(this)),
|
||||||
|
m_sortProxyModel(new FolderSortProxyModel(m_fileSystemModel)),
|
||||||
m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
|
m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
|
||||||
m_showBreadCrumbsAction(new QAction(tr("Show Bread Crumbs"), this)),
|
m_showBreadCrumbsAction(new QAction(tr("Show Bread Crumbs"), this)),
|
||||||
|
m_showFoldersOnTopAction(new QAction(tr("Show Folders on Top"), this)),
|
||||||
m_toggleSync(new QToolButton(this)),
|
m_toggleSync(new QToolButton(this)),
|
||||||
m_toggleRootSync(new QToolButton(this)),
|
m_toggleRootSync(new QToolButton(this)),
|
||||||
m_rootSelector(new QComboBox),
|
m_rootSelector(new QComboBox),
|
||||||
@@ -264,6 +304,9 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
|
|
||||||
setBackgroundRole(QPalette::Base);
|
setBackgroundRole(QPalette::Base);
|
||||||
setAutoFillBackground(true);
|
setAutoFillBackground(true);
|
||||||
|
m_sortProxyModel->setSourceModel(m_fileSystemModel);
|
||||||
|
m_sortProxyModel->setSortRole(FolderNavigationModel::IsFolderRole);
|
||||||
|
m_sortProxyModel->sort(0);
|
||||||
m_fileSystemModel->setResolveSymlinks(false);
|
m_fileSystemModel->setResolveSymlinks(false);
|
||||||
m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider());
|
m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider());
|
||||||
QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot;
|
QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot;
|
||||||
@@ -275,9 +318,11 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
setHiddenFilesFilter(false);
|
setHiddenFilesFilter(false);
|
||||||
m_showBreadCrumbsAction->setCheckable(true);
|
m_showBreadCrumbsAction->setCheckable(true);
|
||||||
setShowBreadCrumbs(true);
|
setShowBreadCrumbs(true);
|
||||||
|
m_showFoldersOnTopAction->setCheckable(true);
|
||||||
|
setShowFoldersOnTop(true);
|
||||||
m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
m_listView->setIconSize(QSize(16,16));
|
m_listView->setIconSize(QSize(16,16));
|
||||||
m_listView->setModel(m_fileSystemModel);
|
m_listView->setModel(m_sortProxyModel);
|
||||||
m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
m_listView->setDragEnabled(true);
|
m_listView->setDragEnabled(true);
|
||||||
m_listView->setDragDropMode(QAbstractItemView::DragOnly);
|
m_listView->setDragDropMode(QAbstractItemView::DragOnly);
|
||||||
@@ -319,17 +364,21 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
// connections
|
// connections
|
||||||
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
||||||
this, &FolderNavigationWidget::handleCurrentEditorChanged);
|
this, &FolderNavigationWidget::handleCurrentEditorChanged);
|
||||||
connect(m_listView, &QAbstractItemView::activated,
|
connect(m_listView, &QAbstractItemView::activated, this, [this](const QModelIndex &index) {
|
||||||
this, [this](const QModelIndex &index) { openItem(index); });
|
openItem(m_sortProxyModel->mapToSource(index));
|
||||||
|
});
|
||||||
// use QueuedConnection for updating crumble path, because that can scroll, which doesn't
|
// use QueuedConnection for updating crumble path, because that can scroll, which doesn't
|
||||||
// work well when done directly in currentChanged (the wrong item can get highlighted)
|
// work well when done directly in currentChanged (the wrong item can get highlighted)
|
||||||
connect(m_listView->selectionModel(),
|
connect(m_listView->selectionModel(),
|
||||||
&QItemSelectionModel::currentChanged,
|
&QItemSelectionModel::currentChanged,
|
||||||
this,
|
this,
|
||||||
&FolderNavigationWidget::setCrumblePath,
|
[this](const QModelIndex ¤t, const QModelIndex &previous) {
|
||||||
|
setCrumblePath(m_sortProxyModel->mapToSource(current),
|
||||||
|
m_sortProxyModel->mapToSource(previous));
|
||||||
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FileName &path) {
|
connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FileName &path) {
|
||||||
const QModelIndex rootIndex = m_listView->rootIndex();
|
const QModelIndex rootIndex = m_sortProxyModel->mapToSource(m_listView->rootIndex());
|
||||||
const QModelIndex fileIndex = m_fileSystemModel->index(path.toString());
|
const QModelIndex fileIndex = m_fileSystemModel->index(path.toString());
|
||||||
if (!isChildOf(fileIndex, rootIndex))
|
if (!isChildOf(fileIndex, rootIndex))
|
||||||
selectBestRootForFile(path);
|
selectBestRootForFile(path);
|
||||||
@@ -343,6 +392,10 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
&QAction::toggled,
|
&QAction::toggled,
|
||||||
this,
|
this,
|
||||||
&FolderNavigationWidget::setShowBreadCrumbs);
|
&FolderNavigationWidget::setShowBreadCrumbs);
|
||||||
|
connect(m_showFoldersOnTopAction,
|
||||||
|
&QAction::toggled,
|
||||||
|
this,
|
||||||
|
&FolderNavigationWidget::setShowFoldersOnTop);
|
||||||
connect(m_toggleSync,
|
connect(m_toggleSync,
|
||||||
&QAbstractButton::clicked,
|
&QAbstractButton::clicked,
|
||||||
this,
|
this,
|
||||||
@@ -356,8 +409,8 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
const auto directory = m_rootSelector->itemData(index).value<Utils::FileName>();
|
const auto directory = m_rootSelector->itemData(index).value<Utils::FileName>();
|
||||||
m_rootSelector->setToolTip(directory.toString());
|
m_rootSelector->setToolTip(directory.toString());
|
||||||
setRootDirectory(directory);
|
setRootDirectory(directory);
|
||||||
const QModelIndex rootIndex = m_listView->rootIndex();
|
const QModelIndex rootIndex = m_sortProxyModel->mapToSource(m_listView->rootIndex());
|
||||||
const QModelIndex fileIndex = m_listView->currentIndex();
|
const QModelIndex fileIndex = m_sortProxyModel->mapToSource(m_listView->currentIndex());
|
||||||
if (!isChildOf(fileIndex, rootIndex))
|
if (!isChildOf(fileIndex, rootIndex))
|
||||||
selectFile(directory);
|
selectFile(directory);
|
||||||
});
|
});
|
||||||
@@ -382,6 +435,13 @@ void FolderNavigationWidget::setShowBreadCrumbs(bool show)
|
|||||||
m_crumbLabel->setVisible(show);
|
m_crumbLabel->setVisible(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderNavigationWidget::setShowFoldersOnTop(bool onTop)
|
||||||
|
{
|
||||||
|
m_showFoldersOnTopAction->setChecked(onTop);
|
||||||
|
m_sortProxyModel->setSortRole(onTop ? FolderNavigationModel::IsFolderRole
|
||||||
|
: QFileSystemModel::FileNameRole);
|
||||||
|
}
|
||||||
|
|
||||||
static bool itemLessThan(QComboBox *combo,
|
static bool itemLessThan(QComboBox *combo,
|
||||||
int index,
|
int index,
|
||||||
const FolderNavigationWidgetFactory::RootDirectory &directory)
|
const FolderNavigationWidgetFactory::RootDirectory &directory)
|
||||||
@@ -433,7 +493,7 @@ void FolderNavigationWidget::removeRootDirectory(const QString &id)
|
|||||||
|
|
||||||
void FolderNavigationWidget::addNewItem()
|
void FolderNavigationWidget::addNewItem()
|
||||||
{
|
{
|
||||||
const QModelIndex current = m_listView->currentIndex();
|
const QModelIndex current = m_sortProxyModel->mapToSource(m_listView->currentIndex());
|
||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
return;
|
return;
|
||||||
const auto filePath = Utils::FileName::fromString(m_fileSystemModel->filePath(current));
|
const auto filePath = Utils::FileName::fromString(m_fileSystemModel->filePath(current));
|
||||||
@@ -448,7 +508,7 @@ void FolderNavigationWidget::addNewItem()
|
|||||||
void FolderNavigationWidget::editCurrentItem()
|
void FolderNavigationWidget::editCurrentItem()
|
||||||
{
|
{
|
||||||
const QModelIndex current = m_listView->currentIndex();
|
const QModelIndex current = m_listView->currentIndex();
|
||||||
if (m_fileSystemModel->flags(current) & Qt::ItemIsEditable)
|
if (m_listView->model()->flags(current) & Qt::ItemIsEditable)
|
||||||
m_listView->edit(current);
|
m_listView->edit(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +527,7 @@ static QVector<FolderNode *> removableFolderNodes(const Utils::FileName &filePat
|
|||||||
|
|
||||||
void FolderNavigationWidget::removeCurrentItem()
|
void FolderNavigationWidget::removeCurrentItem()
|
||||||
{
|
{
|
||||||
const QModelIndex current = m_listView->currentIndex();
|
const QModelIndex current = m_sortProxyModel->mapToSource(m_listView->currentIndex());
|
||||||
if (!current.isValid() || m_fileSystemModel->isDir(current))
|
if (!current.isValid() || m_fileSystemModel->isDir(current))
|
||||||
return;
|
return;
|
||||||
const QString filePath = m_fileSystemModel->filePath(current);
|
const QString filePath = m_fileSystemModel->filePath(current);
|
||||||
@@ -543,7 +603,8 @@ void FolderNavigationWidget::selectBestRootForFile(const Utils::FileName &filePa
|
|||||||
|
|
||||||
void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
|
void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
|
||||||
{
|
{
|
||||||
const QModelIndex fileIndex = m_fileSystemModel->index(filePath.toString());
|
const QModelIndex fileIndex = m_sortProxyModel->mapFromSource(
|
||||||
|
m_fileSystemModel->index(filePath.toString()));
|
||||||
if (fileIndex.isValid() || filePath.isEmpty() /* Computer root */) {
|
if (fileIndex.isValid() || filePath.isEmpty() /* Computer root */) {
|
||||||
// TODO This only scrolls to the right position if all directory contents are loaded.
|
// TODO This only scrolls to the right position if all directory contents are loaded.
|
||||||
// Unfortunately listening to directoryLoaded was still not enough (there might also
|
// Unfortunately listening to directoryLoaded was still not enough (there might also
|
||||||
@@ -564,7 +625,8 @@ void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
|
|||||||
|
|
||||||
void FolderNavigationWidget::setRootDirectory(const Utils::FileName &directory)
|
void FolderNavigationWidget::setRootDirectory(const Utils::FileName &directory)
|
||||||
{
|
{
|
||||||
const QModelIndex index = m_fileSystemModel->setRootPath(directory.toString());
|
const QModelIndex index = m_sortProxyModel->mapFromSource(
|
||||||
|
m_fileSystemModel->setRootPath(directory.toString()));
|
||||||
m_listView->setRootIndex(index);
|
m_listView->setRootIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +686,8 @@ void FolderNavigationWidget::createNewFolder(const QModelIndex &parent)
|
|||||||
const Utils::FileName name = Utils::makeUniquelyNumbered(Utils::FileName::fromString(baseName),
|
const Utils::FileName name = Utils::makeUniquelyNumbered(Utils::FileName::fromString(baseName),
|
||||||
existingItems);
|
existingItems);
|
||||||
// create directory and edit
|
// create directory and edit
|
||||||
const QModelIndex index = m_fileSystemModel->mkdir(parent, name.toString());
|
const QModelIndex index = m_sortProxyModel->mapFromSource(
|
||||||
|
m_fileSystemModel->mkdir(parent, name.toString()));
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return;
|
return;
|
||||||
m_listView->setCurrentIndex(index);
|
m_listView->setCurrentIndex(index);
|
||||||
@@ -662,7 +725,7 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
{
|
{
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
// Open current item
|
// Open current item
|
||||||
const QModelIndex current = m_listView->currentIndex();
|
const QModelIndex current = m_sortProxyModel->mapToSource(m_listView->currentIndex());
|
||||||
const bool hasCurrentItem = current.isValid();
|
const bool hasCurrentItem = current.isValid();
|
||||||
QAction *actionOpenFile = nullptr;
|
QAction *actionOpenFile = nullptr;
|
||||||
QAction *actionOpenProjects = nullptr;
|
QAction *actionOpenProjects = nullptr;
|
||||||
@@ -753,6 +816,11 @@ bool FolderNavigationWidget::isShowingBreadCrumbs() const
|
|||||||
return m_showBreadCrumbsAction->isChecked();
|
return m_showBreadCrumbsAction->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FolderNavigationWidget::isShowingFoldersOnTop() const
|
||||||
|
{
|
||||||
|
return m_showFoldersOnTopAction->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
|
QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
|
||||||
{
|
{
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
@@ -812,6 +880,7 @@ Core::NavigationView FolderNavigationWidgetFactory::createWidget()
|
|||||||
auto filterMenu = new QMenu(filter);
|
auto filterMenu = new QMenu(filter);
|
||||||
filterMenu->addAction(fnw->m_filterHiddenFilesAction);
|
filterMenu->addAction(fnw->m_filterHiddenFilesAction);
|
||||||
filterMenu->addAction(fnw->m_showBreadCrumbsAction);
|
filterMenu->addAction(fnw->m_showBreadCrumbsAction);
|
||||||
|
filterMenu->addAction(fnw->m_showFoldersOnTopAction);
|
||||||
filter->setMenu(filterMenu);
|
filter->setMenu(filterMenu);
|
||||||
n.dockToolBarWidgets << filter << fnw->m_toggleSync;
|
n.dockToolBarWidgets << filter << fnw->m_toggleSync;
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class QAction;
|
|||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QFileSystemModel;
|
class QFileSystemModel;
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
@@ -100,10 +101,12 @@ public:
|
|||||||
bool autoSynchronization() const;
|
bool autoSynchronization() const;
|
||||||
bool hiddenFilesFilter() const;
|
bool hiddenFilesFilter() const;
|
||||||
bool isShowingBreadCrumbs() const;
|
bool isShowingBreadCrumbs() const;
|
||||||
|
bool isShowingFoldersOnTop() const;
|
||||||
|
|
||||||
void setAutoSynchronization(bool sync);
|
void setAutoSynchronization(bool sync);
|
||||||
void toggleAutoSynchronization();
|
void toggleAutoSynchronization();
|
||||||
void setShowBreadCrumbs(bool show);
|
void setShowBreadCrumbs(bool show);
|
||||||
|
void setShowFoldersOnTop(bool onTop);
|
||||||
|
|
||||||
void insertRootDirectory(const FolderNavigationWidgetFactory::RootDirectory &directory);
|
void insertRootDirectory(const FolderNavigationWidgetFactory::RootDirectory &directory);
|
||||||
void removeRootDirectory(const QString &id);
|
void removeRootDirectory(const QString &id);
|
||||||
@@ -133,8 +136,10 @@ private:
|
|||||||
Core::IContext *m_context = nullptr;
|
Core::IContext *m_context = nullptr;
|
||||||
Utils::NavigationTreeView *m_listView = nullptr;
|
Utils::NavigationTreeView *m_listView = nullptr;
|
||||||
QFileSystemModel *m_fileSystemModel = nullptr;
|
QFileSystemModel *m_fileSystemModel = nullptr;
|
||||||
|
QSortFilterProxyModel *m_sortProxyModel = nullptr;
|
||||||
QAction *m_filterHiddenFilesAction = nullptr;
|
QAction *m_filterHiddenFilesAction = nullptr;
|
||||||
QAction *m_showBreadCrumbsAction = nullptr;
|
QAction *m_showBreadCrumbsAction = nullptr;
|
||||||
|
QAction *m_showFoldersOnTopAction = nullptr;
|
||||||
bool m_autoSync = false;
|
bool m_autoSync = false;
|
||||||
bool m_rootAutoSync = true;
|
bool m_rootAutoSync = true;
|
||||||
QToolButton *m_toggleSync = nullptr;
|
QToolButton *m_toggleSync = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user