Commit 4328898f authored by Eike Ziller's avatar Eike Ziller

Add a bread crumb widget to file system view

Which allows easy navigation to any parent of the current selection.

Task-number: QTCREATORBUG-19203
Change-Id: I97179a4800afd92a21bd0fa466fbfda4ac1a5846
Reviewed-by: Alessandro Portale's avatarAlessandro Portale <alessandro.portale@qt.io>
parent 2453d20d
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 "filecrumblabel.h"
#include <utils/hostosinfo.h>
#include <QDir>
#include <QUrl>
namespace Utils {
FileCrumbLabel::FileCrumbLabel(QWidget *parent)
: QLabel(parent)
{
setTextFormat(Qt::RichText);
setWordWrap(true);
connect(this, &QLabel::linkActivated, this, [this](const QString &url) {
emit pathClicked(FileName::fromString(QUrl(url).toLocalFile()));
});
setPath(FileName());
}
static QString linkForPath(const FileName &path, const QString &display)
{
return "<a href=\""
+ QUrl::fromLocalFile(path.toString()).toString(QUrl::FullyEncoded) + "\">"
+ display + "</a>";
}
void FileCrumbLabel::setPath(const FileName &path)
{
QStringList links;
FileName current = path;
while (!current.isEmpty()) {
const QString fileName = current.fileName();
if (!fileName.isEmpty()) {
links.prepend(linkForPath(current, fileName));
} else if (HostOsInfo::isWindowsHost() && QDir(current.toString()).isRoot()) {
// Only on Windows add the drive letter, without the '/' at the end
QString display = current.toString();
if (display.endsWith('/'))
display.chop(1);
links.prepend(linkForPath(current, display));
}
current = current.parentDir();
}
const auto pathSeparator = QLatin1String(HostOsInfo::isWindowsHost() ? " \\ " : " / ");
const QString prefix = HostOsInfo::isWindowsHost() ? QString("&nbsp;\\ ") : QString("&nbsp;/ ");
setText(prefix + links.join(pathSeparator));
}
} // Utils
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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.
**
****************************************************************************/
#pragma once
#include "utils_global.h"
#include "fileutils.h"
#include <QLabel>
namespace Utils {
class QTCREATOR_UTILS_EXPORT FileCrumbLabel : public QLabel
{
Q_OBJECT
public:
FileCrumbLabel(QWidget *parent = nullptr);
void setPath(const FileName &path);
signals:
void pathClicked(const FileName &path);
};
} // Utils
......@@ -94,12 +94,13 @@ public:
FileName &appendString(const QString &str);
FileName &appendString(QChar str);
using QString::size;
using QString::chop;
using QString::clear;
using QString::count;
using QString::length;
using QString::isEmpty;
using QString::isNull;
using QString::clear;
using QString::length;
using QString::size;
private:
FileName(const QString &string);
};
......
......@@ -118,7 +118,8 @@ SOURCES += \
$$PWD/highlightingitemdelegate.cpp \
$$PWD/fuzzymatcher.cpp \
$$PWD/textutils.cpp \
$$PWD/url.cpp
$$PWD/url.cpp \
$$PWD/filecrumblabel.cpp
win32:SOURCES += $$PWD/consoleprocess_win.cpp
else:SOURCES += $$PWD/consoleprocess_unix.cpp
......@@ -250,7 +251,8 @@ HEADERS += \
$$PWD/fuzzymatcher.h \
$$PWD/textutils.h \
$$PWD/predicates.h \
$$PWD/url.h
$$PWD/url.h \
$$PWD/filecrumblabel.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/projectintropage.ui \
......
......@@ -99,6 +99,8 @@ Project {
"fancylineedit.h",
"fancymainwindow.cpp",
"fancymainwindow.h",
"filecrumblabel.cpp",
"filecrumblabel.h",
"fileinprojectfinder.cpp",
"fileinprojectfinder.h",
"filenamevalidatinglineedit.cpp",
......
......@@ -42,6 +42,7 @@
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
#include <utils/filecrumblabel.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/navigationtreeview.h>
......@@ -49,6 +50,7 @@
#include <QComboBox>
#include <QHeaderView>
#include <QScrollBar>
#include <QSize>
#include <QTimer>
#include <QFileSystemModel>
......@@ -106,6 +108,19 @@ static void showOnlyFirstColumn(QTreeView *view)
view->setColumnHidden(i, true);
}
static bool isChildOf(const QModelIndex &index, const QModelIndex &parent)
{
if (index == parent)
return true;
QModelIndex current = index;
while (current.isValid()) {
current = current.parent();
if (current == parent)
return true;
}
return false;
}
/*!
\class FolderNavigationWidget
......@@ -118,7 +133,8 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
m_fileSystemModel(new FolderNavigationModel(this)),
m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
m_toggleSync(new QToolButton(this)),
m_rootSelector(new QComboBox)
m_rootSelector(new QComboBox),
m_crumbLabel(new Utils::FileCrumbLabel(this))
{
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
......@@ -146,6 +162,9 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
auto layout = new QVBoxLayout();
layout->addWidget(selectorWidget);
layout->addSpacing(4);
layout->addWidget(m_crumbLabel);
layout->addSpacing(4);
layout->addWidget(m_listView);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
......@@ -159,8 +178,24 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
// connections
connect(m_listView, &QAbstractItemView::activated,
this, [this](const QModelIndex &index) { openItem(index); });
connect(m_filterHiddenFilesAction, &QAction::toggled,
this, &FolderNavigationWidget::setHiddenFilesFilter);
connect(m_listView->selectionModel(),
&QItemSelectionModel::currentChanged,
this,
[this](const QModelIndex &current, const QModelIndex &) {
m_crumbLabel->setPath(
Utils::FileName::fromString(m_fileSystemModel->filePath(current)).parentDir());
});
connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FileName &path) {
const QModelIndex rootIndex = m_listView->rootIndex();
const QModelIndex fileIndex = m_fileSystemModel->index(path.toString());
if (!isChildOf(fileIndex, rootIndex))
selectBestRootForFile(path);
selectFile(path);
});
connect(m_filterHiddenFilesAction,
&QAction::toggled,
this,
&FolderNavigationWidget::setHiddenFilesFilter);
connect(m_toggleSync, &QAbstractButton::clicked,
this, &FolderNavigationWidget::toggleAutoSynchronization);
connect(m_rootSelector,
......@@ -262,11 +297,14 @@ void FolderNavigationWidget::setCurrentEditor(Core::IEditor *editor)
if (!editor || editor->document()->filePath().isEmpty() || editor->document()->isTemporary())
return;
const Utils::FileName filePath = editor->document()->filePath();
// switch to most fitting root
selectBestRootForFile(filePath);
selectFile(filePath);
}
void FolderNavigationWidget::selectBestRootForFile(const Utils::FileName &filePath)
{
const int bestRootIndex = bestRootForFile(filePath);
m_rootSelector->setCurrentIndex(bestRootIndex);
// select
selectFile(filePath);
}
void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
......@@ -280,7 +318,12 @@ void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
m_listView->setCurrentIndex(fileIndex);
QTimer::singleShot(200, this, [this, filePath] {
const QModelIndex fileIndex = m_fileSystemModel->index(filePath.toString());
m_listView->scrollTo(fileIndex);
if (fileIndex == m_listView->rootIndex()) {
m_listView->horizontalScrollBar()->setValue(0);
m_listView->verticalScrollBar()->setValue(0);
} else {
m_listView->scrollTo(fileIndex);
}
});
}
}
......
......@@ -35,6 +35,7 @@ namespace Core { class IEditor; }
namespace Utils {
class NavigationTreeView;
class FileCrumbLabel;
}
QT_BEGIN_NAMESPACE
......@@ -103,6 +104,7 @@ protected:
private:
void setHiddenFilesFilter(bool filter);
void setCurrentEditor(Core::IEditor *editor);
void selectBestRootForFile(const Utils::FileName &filePath);
void selectFile(const Utils::FileName &filePath);
void setRootDirectory(const Utils::FileName &directory);
int bestRootForFile(const Utils::FileName &filePath);
......@@ -116,6 +118,7 @@ private:
bool m_autoSync = false;
QToolButton *m_toggleSync = nullptr;
QComboBox *m_rootSelector = nullptr;
Utils::FileCrumbLabel *m_crumbLabel = nullptr;
// FolderNavigationWidgetFactory needs private members to build a menu
friend class FolderNavigationWidgetFactory;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment