Commit 893cccb7 authored by Waqar Ahmed's avatar Waqar Ahmed Committed by Christoph Cullmann
Browse files

Introduce KateNavigationBar

This change introduces a new UI element i.e., Navigation Bar which is
essentially your file url broken into breadcrumbs, where each
breadcrumb allows you to navigate the filesystem from that point
onwards. This allows for faster file navigation. In future, we can add
symbol navigation to this.
parent fe2115bd
......@@ -99,6 +99,8 @@ target_sources(
kateoutputview.cpp
katestashmanager.cpp
kateurlbar.cpp
)
# Executable only adds the main definition.
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE gui SYSTEM "kpartgui.dtd">
<gui name="kate" version="89" translationDomain="kate">
<gui name="kate" version="90" translationDomain="kate">
<MenuBar>
<Menu name="file" noMerge="1">
<text>&amp;File</text>
......@@ -108,6 +108,7 @@
<text>&amp;Settings</text>
<Action name="settings_show_tab_bar" append="show_merge"/>
<Action name="settings_show_full_path" append="show_merge"/>
<Action name="settings_show_url_nav_bar" append="show_merge"/>
<Action name="colorscheme_menu"/>
</Menu>
</MenuBar>
......
......@@ -224,6 +224,12 @@ void KateMainWindow::setupImportantActions()
connect(m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption()));
m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption"));
m_paShowUrlNavBar = new KToggleAction(i18n("Show Navigation Bar"), this);
actionCollection()->addAction(QStringLiteral("settings_show_url_nav_bar"), m_paShowUrlNavBar);
connect(m_paShowUrlNavBar, &QAction::toggled, this, [this](bool v) {
m_viewManager->setShowUrlNavBar(v);
});
// Load themes
actionCollection()->addAction(QStringLiteral("colorscheme_menu"), new KateColorSchemeChooser(actionCollection()));
......@@ -663,10 +669,12 @@ void KateMainWindow::readOptions()
m_paShowStatusBar->setChecked(generalGroup.readEntry("Show Status Bar", true));
m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true));
m_paShowTabBar->setChecked(generalGroup.readEntry("Show Tab Bar", true));
m_paShowUrlNavBar->setChecked(generalGroup.readEntry("Show Url Nav Bar", true));
// emit signal to hide/show statusbars
toggleShowStatusBar();
toggleShowTabBar();
m_viewManager->setShowUrlNavBar(m_paShowUrlNavBar->isChecked());
}
void KateMainWindow::saveOptions()
......@@ -683,6 +691,7 @@ void KateMainWindow::saveOptions()
generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked());
generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked());
generalGroup.writeEntry("Show Tab Bar", m_paShowTabBar->isChecked());
generalGroup.writeEntry("Show Url Nav Bar", m_paShowUrlNavBar->isChecked());
}
void KateMainWindow::toggleShowMenuBar(bool showMessage)
......
......@@ -563,6 +563,7 @@ private:
KToggleAction *m_paShowMenuBar = nullptr;
KToggleAction *m_paShowStatusBar = nullptr;
KToggleAction *m_paShowTabBar = nullptr;
KToggleAction *m_paShowUrlNavBar = nullptr;
QWidget *m_bottomViewBarContainer = nullptr;
KateContainerStackedLayout *m_bottomContainerStack = nullptr;
......
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kateurlbar.h"
#include "kateviewmanager.h"
#include "kateviewspace.h"
#include <KTextEditor/Document>
#include <KTextEditor/View>
#include <QAbstractListModel>
#include <QAction>
#include <QDir>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QListView>
#include <QMenu>
#include <QMimeDatabase>
#include <QScrollBar>
#include <QToolButton>
#include <QUrl>
class DirFilesModel : public QAbstractListModel
{
Q_OBJECT
public:
DirFilesModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
{
}
enum Role { FileInfo = Qt::UserRole + 1 };
int rowCount(const QModelIndex & = {}) const override
{
return m_fileInfos.size();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (!index.isValid()) {
return {};
}
const auto &fi = m_fileInfos.at(index.row());
if (role == Qt::DisplayRole) {
return fi.fileName();
} else if (role == Qt::DecorationRole) {
if (fi.isDir()) {
return QIcon(QIcon::fromTheme(QStringLiteral("folder")));
} else if (fi.isFile()) {
return QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(fi).iconName());
}
} else if (role == FileInfo) {
return QVariant::fromValue(fi);
}
return {};
}
void setDir(const QDir &dir)
{
beginResetModel();
const auto fileInfos = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::Hidden);
for (const auto &fi : fileInfos) {
if (fi.isDir()) {
m_fileInfos << fi;
} else if (QMimeDatabase().mimeTypeForFile(fi).inherits(QStringLiteral("text/plain"))) {
m_fileInfos << fi;
}
}
endResetModel();
}
private:
QList<QFileInfo> m_fileInfos;
};
class DirFilesList : public QMenu
{
Q_OBJECT
public:
DirFilesList(QWidget *parent)
: QMenu(parent)
{
m_list.setModel(&m_model);
m_list.setResizeMode(QListView::Adjust);
m_list.setViewMode(QListView::ListMode);
m_list.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_list.setFrameStyle(QFrame::NoFrame);
auto *l = new QVBoxLayout(this);
l->setContentsMargins({});
l->addWidget(&m_list);
connect(&m_list, &QListView::clicked, this, &DirFilesList::onClicked);
}
void setDir(const QDir &d)
{
m_model.setDir(d);
updateGeometry();
}
void updateGeometry()
{
auto s = m_list.sizeHintForRow(0);
auto c = m_model.rowCount();
const auto h = s * c + (s / 2);
const auto vScroll = m_list.verticalScrollBar();
int w = m_list.sizeHintForColumn(0) + (vScroll ? vScroll->height() / 2 : 0);
setFixedSize(qMin(w, 300), qMin(h, 600));
}
void onClicked(const QModelIndex &idx)
{
if (!idx.isValid()) {
return;
}
const auto fi = idx.data(DirFilesModel::FileInfo).value<QFileInfo>();
if (fi.isDir()) {
setDir(QDir(fi.absoluteFilePath()));
} else if (fi.isFile()) {
const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
hide();
Q_EMIT openUrl(url);
}
}
Q_SIGNALS:
void openUrl(const QUrl &url);
private:
QListView m_list;
DirFilesModel m_model;
};
KateUrlBar::KateUrlBar(KateViewSpace *parent)
: QWidget(parent)
{
setFixedHeight(25);
setContentsMargins({});
m_layout = new QHBoxLayout(this);
m_layout->setContentsMargins({});
m_layout->setSpacing(0);
auto *vm = parent->viewManger();
connect(vm, &KateViewManager::viewChanged, this, [this, vm](KTextEditor::View *v) {
if (vm && !vm->showUrlNavBar()) {
return;
}
m_paths.clear();
QLayoutItem *l;
while ((l = m_layout->takeAt(0)) != nullptr) {
delete l->widget();
delete l;
}
const auto url = v->document()->url();
if (url.isEmpty()) {
hide();
return;
}
auto res = splittedUrl(url);
const auto &file = res.first;
const auto &dirs = res.second;
if (dirs.isEmpty() || file.isEmpty()) {
hide();
return;
}
for (const auto &dir : dirs) {
m_layout->addWidget(dirButton(dir.name, dir.path));
m_layout->addWidget(separatorLabel());
}
m_layout->addWidget(fileLabel(file));
m_layout->addStretch();
if (isHidden())
show();
});
connect(vm, &KateViewManager::showUrlNavBarChanged, this, [this](bool show) {
setHidden(!show);
});
setHidden(!vm->showUrlNavBar());
}
std::pair<QString, QVector<KateUrlBar::DirNamePath>> KateUrlBar::splittedUrl(const QUrl &u)
{
QString s = u.toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile);
const int slashIndex = s.lastIndexOf(QLatin1Char('/'));
if (slashIndex == -1) {
return {};
}
const QString fileName = s.mid(slashIndex + 1);
QDir dir(s);
QVector<DirNamePath> dirsList;
while (dir.cdUp()) {
if (dir.dirName().isEmpty()) {
continue;
}
DirNamePath dnp{dir.dirName(), dir.absolutePath()};
dirsList.push_back(dnp);
}
std::reverse(dirsList.begin(), dirsList.end());
return {fileName, dirsList};
}
QLabel *KateUrlBar::separatorLabel()
{
QLabel *sep = new QLabel(this);
sep->setPixmap(QIcon::fromTheme(QStringLiteral("arrow-right")).pixmap(QSize(16, 16)));
return sep;
}
QToolButton *KateUrlBar::dirButton(const QString &dirName, const QString &path)
{
QToolButton *tb = new QToolButton(this);
tb->setText(dirName);
tb->setArrowType(Qt::RightArrow);
tb->setToolButtonStyle(Qt::ToolButtonTextOnly);
tb->setAutoRaise(true);
connect(tb, &QToolButton::clicked, this, &KateUrlBar::pathClicked);
m_paths.push_back({tb, path});
return tb;
}
QLabel *KateUrlBar::fileLabel(const QString &file)
{
auto l = new QLabel(file);
auto font = l->font();
font.setPointSize(font.pointSize() - 1);
l->setFont(font);
l->setAlignment(Qt::AlignCenter);
l->setContentsMargins({6, 0, 0, 0});
return l;
}
void KateUrlBar::pathClicked()
{
auto button = sender();
if (!button) {
return;
}
// Find the path that this toolbutton refers to
auto it = std::find_if(m_paths.begin(), m_paths.end(), [button](const std::pair<QToolButton *, QString> &tbPath) {
return tbPath.first == button;
});
// not found?
if (it == m_paths.end()) {
return;
}
const QToolButton *tb = it->first;
const QString path = it->second;
QDir d(path);
DirFilesList m(this);
connect(&m, &DirFilesList::openUrl, this, &KateUrlBar::openUrlRequested);
m.setDir(d);
auto pos = tb->mapToGlobal(tb->pos()) - tb->pos();
pos.setY(pos.y() + tb->height());
m.exec(pos);
}
#include "kateurlbar.moc"
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KATE_URL_BAR_H
#define KATE_URL_BAR_H
#include "kateviewspace.h"
#include <QFrame>
class KateUrlBar : public QWidget
{
Q_OBJECT
public:
explicit KateUrlBar(KateViewSpace *parent = nullptr);
Q_SIGNALS:
void openUrlRequested(const QUrl &url);
private:
// helper struct containing dir name and path
struct DirNamePath {
QString name;
QString path;
};
std::pair<QString, QVector<DirNamePath>> splittedUrl(const QUrl &u);
class QLabel *separatorLabel();
class QToolButton *dirButton(const QString &dirName, const QString &path);
class QLabel *fileLabel(const QString &file);
class QHBoxLayout *m_layout;
void pathClicked();
QVector<std::pair<QToolButton *, QString>> m_paths;
};
#endif
......@@ -915,6 +915,19 @@ void KateViewManager::onViewSpaceEmptied(KateViewSpace *vs)
}
}
void KateViewManager::setShowUrlNavBar(bool show)
{
if (show != m_showUrlNavBar) {
m_showUrlNavBar = show;
Q_EMIT showUrlNavBarChanged(show);
}
}
bool KateViewManager::showUrlNavBar() const
{
return m_showUrlNavBar;
}
bool KateViewManager::viewsInSameViewSpace(KTextEditor::View *view1, KTextEditor::View *view2)
{
if (!view1 || !view2) {
......
......@@ -100,6 +100,8 @@ Q_SIGNALS:
void historyBackEnabled(bool e);
void historyForwardEnabled(bool e);
void showUrlNavBarChanged(bool);
public:
/**
* create and activate a new view for doc, if doc == 0, then
......@@ -274,6 +276,9 @@ public Q_SLOTS:
void onViewSpaceEmptied(KateViewSpace *vs);
void setShowUrlNavBar(bool show);
bool showUrlNavBar() const;
private:
KateMainWindow *m_mainWindow;
......@@ -292,6 +297,8 @@ private:
bool m_activeViewRunning;
bool m_showUrlNavBar = false;
int m_splitterIndex = 0; // used during saving splitter config.
/**
......
......@@ -14,8 +14,10 @@
#include "katemainwindow.h"
#include "katesessionmanager.h"
#include "kateupdatedisabler.h"
#include "kateurlbar.h"
#include "kateviewmanager.h"
#include "tabmimedata.h"
#include <KActionCollection>
#include <KAcceleratorManager>
......@@ -119,6 +121,12 @@ KateViewSpace::KateViewSpace(KateViewManager *viewManager, QWidget *parent, cons
layout->addLayout(hLayout);
// END tab bar
m_urlBar = new KateUrlBar(this);
connect(m_urlBar, &KateUrlBar::openUrlRequested, this, [this](const QUrl &url) {
m_viewManager->openUrl(url);
});
layout->addWidget(m_urlBar);
stack = new QStackedWidget(this);
stack->setFocus();
stack->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding));
......
......@@ -294,6 +294,8 @@ private:
// rubber band to indicate drag and drop
std::unique_ptr<class QRubberBand> m_dropIndicator;
class KateUrlBar *m_urlBar = nullptr;
friend class LocationHistoryTest;
};
......
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