Commit 141b08f8 authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Unify doc & widget handling

With this change doc and widget handling is unified even more.

Features like:
- Alt + Left / Up / Down / Right should work when there are widgets
 (tests needed)
- Widgets are now part of the tab lru handling
parent 2e84edf9
Pipeline #259076 failed with stage
in 13 minutes and 1 second
......@@ -702,8 +702,11 @@ void KateFileTree::slotDocumentPrev()
}
if (prev.isValid()) {
KTextEditor::Document *doc = m_proxyModel->docFromIndex(prev);
Q_EMIT activateDocument(doc);
if (auto *doc = m_proxyModel->docFromIndex(prev)) {
Q_EMIT activateDocument(doc);
} else if (auto *w = prev.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) {
Q_EMIT activateWidget(w);
}
}
}
......@@ -774,8 +777,11 @@ void KateFileTree::slotDocumentNext()
}
if (next.isValid()) {
KTextEditor::Document *doc = m_proxyModel->docFromIndex(next);
Q_EMIT activateDocument(doc);
if (auto *doc = m_proxyModel->docFromIndex(next)) {
Q_EMIT activateDocument(doc);
} else if (auto *w = next.data(KateFileTreeModel::WidgetRole).value<QWidget *>()) {
Q_EMIT activateWidget(w);
}
}
}
......
......@@ -172,7 +172,7 @@ void KateViewManagementTests::testMoveViewBetweenViewspaces()
auto dest = vm->m_viewSpaceList.front();
QVERIFY(src != dest);
vm->moveViewToViewSpace(dest, src, vm->activeView()->document());
QTest::qWait(100);
qApp->processEvents();
// after moving we should have 2 views but only one viewspace left
QCOMPARE(vm->m_viewSpaceList.size(), 1);
......
#ifndef KATE_DOC_OR_WIDGET
#define KATE_DOC_OR_WIDGET
#include <KTextEditor/Document>
#include <QWidget>
#include <variant>
// TODO: Find a better name for this class
// Just a helper class which we use internally to manage widgets/docs
class DocOrWidget : public std::variant<KTextEditor::Document *, QWidget *>
{
public:
using variant::variant;
auto *doc() const
{
return std::holds_alternative<KTextEditor::Document *>(*this) ? std::get<KTextEditor::Document *>(*this) : nullptr;
}
auto *widget() const
{
return std::holds_alternative<QWidget *>(*this) ? std::get<QWidget *>(*this) : nullptr;
}
QObject *qobject() const
{
return doc() ? static_cast<QObject *>(doc()) : static_cast<QObject *>(widget());
}
bool operator==(KTextEditor::Document *doc) const
{
return this->doc() == doc;
}
bool operator==(QWidget *w) const
{
return this->widget() == w;
}
static DocOrWidget null()
{
DocOrWidget d = static_cast<KTextEditor::Document *>(nullptr);
return d;
}
bool isNull() const
{
return !qobject();
}
void clear()
{
*this = null();
}
};
Q_DECLARE_METATYPE(DocOrWidget)
namespace std
{
template<>
struct hash<DocOrWidget> {
typedef DocOrWidget argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const &s) const noexcept
{
result_type const h1(std::hash<void *>{}(s.qobject()));
return h1;
}
};
}
#endif
......@@ -1368,7 +1368,7 @@ bool KateMainWindow::addWidget(QWidget *widget)
auto vs = m_viewManager->activeViewSpace();
vs->addWidgetAsTab(widget);
Q_EMIT widgetAdded(widget);
m_viewManager->activateView(static_cast<KTextEditor::Document *>(nullptr));
m_viewManager->activateView(widget);
return true;
}
......
......@@ -60,15 +60,15 @@ void KateTabBar::readConfig()
const int tabCountLimit = std::max(cgGeneral.readEntry("Tabbar Tab Limit", 0), 0);
if (m_tabCountLimit != tabCountLimit) {
m_tabCountLimit = tabCountLimit;
const QVector<KTextEditor::Document*> docList = documentList();
const QVector<DocOrWidget> docList = documentList();
if (m_tabCountLimit > 0 && docList.count() > m_tabCountLimit) {
// close N least used tabs
QMap<quint64, KTextEditor::Document*> lruDocs;
for (KTextEditor::Document *doc : docList) {
QMap<quint64, DocOrWidget> lruDocs;
for (const DocOrWidget &doc : docList) {
lruDocs[m_docToLruCounterAndHasTab[doc].first] = doc;
}
int toRemove = docList.count() - m_tabCountLimit;
for (KTextEditor::Document *doc : std::as_const(lruDocs)) {
for (DocOrWidget doc : std::as_const(lruDocs)) {
if (toRemove-- == 0) {
break;
}
......@@ -76,9 +76,9 @@ void KateTabBar::readConfig()
}
} else if (m_docToLruCounterAndHasTab.size() > (size_t)docList.size()) {
// populate N recently user documents
std::map<quint64, KTextEditor::Document*, std::greater<quint64>> mruDocs;
std::map<quint64, DocOrWidget, std::greater<quint64>> mruDocs;
for (const auto &i : m_docToLruCounterAndHasTab) {
KTextEditor::Document *doc = i.first;
DocOrWidget doc = i.first;
if (!docList.contains(doc)) {
mruDocs[i.second.first] = doc;
}
......@@ -88,8 +88,9 @@ void KateTabBar::readConfig()
if (toAdd-- == 0) {
break;
}
KTextEditor::Document *doc = i.second;
setTabDocument(addTab(doc->documentName()), doc);
DocOrWidget doc = i.second;
const auto idx = addTab(doc.doc() ? doc.doc()->documentName() : doc.widget()->windowTitle());
setTabDocument(idx, doc);
}
}
}
......@@ -275,7 +276,8 @@ void KateTabBar::mouseMoveEvent(QMouseEvent *event)
ds << cp.column();
ds << view->document()->url();
auto mime = new TabMimeData(viewSpace, tabDocument(tab));
auto doc = tabDocument(tab).doc(); // We know for sure there is a doc because of above check
auto mime = new TabMimeData(viewSpace, doc);
mime->setData(QStringLiteral("application/kate.tab.mimedata"), data);
QDrag *drag = new QDrag(this);
......@@ -326,15 +328,21 @@ void KateTabBar::contextMenuEvent(QContextMenuEvent *ev)
}
}
void KateTabBar::setTabDocument(int idx, KTextEditor::Document *doc)
void KateTabBar::setTabDocument(int idx, DocOrWidget d)
{
setTabData(idx, QVariant::fromValue(DocOrWidget(doc)));
setTabData(idx, QVariant::fromValue(d));
auto *doc = d.doc();
// BUG: 441340 We need to escape the & because it is used for accelerators/shortcut mnemonic by default
QString tabName = doc->documentName();
QString tabName = d.doc() ? d.doc()->documentName() : d.widget()->windowTitle();
tabName.replace(QLatin1Char('&'), QLatin1String("&&"));
setTabText(idx, tabName);
setTabToolTip(idx, doc->url().isValid() ? doc->url().toDisplayString(QUrl::PreferLocalFile) : doc->documentName());
setModifiedStateIcon(idx, doc);
if (d.doc()) {
setTabToolTip(idx, doc->url().isValid() ? doc->url().toDisplayString(QUrl::PreferLocalFile) : doc->documentName());
setModifiedStateIcon(idx, doc);
} else {
setTabIcon(idx, d.widget()->windowIcon());
}
}
void KateTabBar::setModifiedStateIcon(int idx, KTextEditor::Document *doc)
......@@ -343,15 +351,15 @@ void KateTabBar::setModifiedStateIcon(int idx, KTextEditor::Document *doc)
setTabIcon(idx, Utils::iconForDocument(doc));
}
void KateTabBar::setCurrentDocument(KTextEditor::Document *doc)
void KateTabBar::setCurrentDocument(DocOrWidget docOrWidget)
{
// in any case: update lru counter for this document, might add new element to hash
// we have a tab after this call, too!
m_docToLruCounterAndHasTab[doc] = std::make_pair(++m_lruCounter, true);
m_docToLruCounterAndHasTab[docOrWidget] = std::make_pair(++m_lruCounter, true);
// do we have a tab for this document?
// if yes => just set as current one
const int existingIndex = documentIdx(doc);
const int existingIndex = documentIdx(docOrWidget);
if (existingIndex != -1) {
setCurrentIndex(existingIndex);
return;
......@@ -360,8 +368,8 @@ void KateTabBar::setCurrentDocument(KTextEditor::Document *doc)
// else: if we are still inside the allowed number of tabs or have no limit
// => create new tab and be done
if ((m_tabCountLimit == 0) || documentTabIndexes().size() < (size_t)m_tabCountLimit) {
m_beingAdded = doc;
insertTab(-1, doc->documentName());
m_beingAdded = docOrWidget;
insertTab(-1, docOrWidget.doc() ? docOrWidget.doc()->documentName() : docOrWidget.widget()->windowTitle());
return;
}
......@@ -371,13 +379,10 @@ void KateTabBar::setCurrentDocument(KTextEditor::Document *doc)
// search for the right tab
quint64 minCounter = static_cast<quint64>(-1);
int indexToReplace = 0;
KTextEditor::Document *docToReplace = nullptr;
DocOrWidget docToReplace = DocOrWidget::null();
for (int idx = 0; idx < count(); idx++) {
QVariant data = tabData(idx);
KTextEditor::Document *doc = data.value<DocOrWidget>().doc();
if (!data.isValid() || !doc) {
continue;
}
DocOrWidget doc = data.value<DocOrWidget>();
const quint64 currentCounter = m_docToLruCounterAndHasTab[doc].first;
if (currentCounter <= minCounter) {
minCounter = currentCounter;
......@@ -390,7 +395,7 @@ void KateTabBar::setCurrentDocument(KTextEditor::Document *doc)
m_docToLruCounterAndHasTab[docToReplace].second = false;
// replace it's data + set it as active
setTabDocument(indexToReplace, doc);
setTabDocument(indexToReplace, docOrWidget);
setCurrentIndex(indexToReplace);
}
......@@ -411,7 +416,7 @@ void KateTabBar::removeDocument(KTextEditor::Document *doc)
// if we have some tab limit, replace the removed tab with the next best document that has none!
if (m_tabCountLimit > 0) {
quint64 maxCounter = 0;
KTextEditor::Document *docToReplace = nullptr;
DocOrWidget docToReplace = static_cast<QWidget *>(nullptr);
for (const auto &lru : m_docToLruCounterAndHasTab) {
// ignore stuff with tabs
if (lru.second.second) {
......@@ -426,7 +431,7 @@ void KateTabBar::removeDocument(KTextEditor::Document *doc)
}
// any document found? replace the tab we want to close and be done
if (docToReplace) {
if (docToReplace.qobject()) {
// mark the replace doc as "has a tab"
m_docToLruCounterAndHasTab[docToReplace].second = true;
......@@ -443,14 +448,14 @@ void KateTabBar::removeDocument(KTextEditor::Document *doc)
removeTab(idx);
}
int KateTabBar::documentIdx(KTextEditor::Document *doc)
int KateTabBar::documentIdx(DocOrWidget doc)
{
for (int idx = 0; idx < count(); idx++) {
QVariant data = tabData(idx);
if (!data.isValid()) {
continue;
}
if (data.value<DocOrWidget>().doc() != doc) {
if (data.value<DocOrWidget>().qobject() != doc.qobject()) {
continue;
}
return idx;
......@@ -458,20 +463,20 @@ int KateTabBar::documentIdx(KTextEditor::Document *doc)
return -1;
}
KTextEditor::Document *KateTabBar::tabDocument(int idx)
DocOrWidget KateTabBar::tabDocument(int idx)
{
QVariant data = ensureValidTabData(idx);
DocOrWidget buttonData = data.value<DocOrWidget>();
KTextEditor::Document *doc = nullptr;
auto doc = DocOrWidget::null();
// The tab got activated before the correct finalization,
// we need to plug the document before returning.
if (buttonData.doc() == nullptr && m_beingAdded) {
if (buttonData.isNull() && !m_beingAdded.isNull()) {
setTabDocument(idx, m_beingAdded);
doc = m_beingAdded;
m_beingAdded = nullptr;
m_beingAdded.clear();
} else {
doc = buttonData.doc();
doc = buttonData;
}
return doc;
......@@ -479,29 +484,19 @@ KTextEditor::Document *KateTabBar::tabDocument(int idx)
void KateTabBar::tabInserted(int idx)
{
if (m_beingAdded) {
if (m_beingAdded.qobject()) {
setTabDocument(idx, m_beingAdded);
}
m_beingAdded = nullptr;
m_beingAdded.clear();
}
QVector<KTextEditor::Document *> KateTabBar::documentList() const
QVector<DocOrWidget> KateTabBar::documentList() const
{
QVector<KTextEditor::Document *> result;
QVector<DocOrWidget> result;
result.reserve(count());
for (int idx = 0; idx < count(); idx++) {
QVariant data = tabData(idx);
if (data.isValid() && data.value<DocOrWidget>().doc()) {
result.append(data.value<DocOrWidget>().doc());
}
result.append(data.value<DocOrWidget>());
}
return result;
}
void KateTabBar::setCurrentWidget(QWidget *widget)
{
int idx = insertTab(-1, widget->windowTitle());
setTabIcon(idx, widget->windowIcon());
setTabData(idx, QVariant::fromValue(DocOrWidget(widget)));
setCurrentIndex(idx);
}
......@@ -8,6 +8,7 @@
#ifndef KATE_TAB_BAR_H
#define KATE_TAB_BAR_H
#include "doc_or_widget.h"
#include <QTabBar>
#include <unordered_map>
......@@ -58,10 +59,10 @@ public:
QVariant ensureValidTabData(int idx);
void setCurrentDocument(KTextEditor::Document *doc);
int documentIdx(KTextEditor::Document *doc);
void setTabDocument(int idx, KTextEditor::Document *doc);
KTextEditor::Document *tabDocument(int idx);
void setCurrentDocument(DocOrWidget);
int documentIdx(DocOrWidget);
void setTabDocument(int idx, DocOrWidget doc);
DocOrWidget tabDocument(int idx);
void removeDocument(KTextEditor::Document *doc);
void setModifiedStateIcon(int idx, KTextEditor::Document *doc);
......@@ -84,12 +85,7 @@ public:
* Returns the document list of this tab bar.
* @return document list in order of tabs
*/
QVector<KTextEditor::Document *> documentList() const;
/**
* Add an "extra" widget to the tab bar which is not a "KTextEditor::View"
*/
void setCurrentWidget(QWidget *widget);
QVector<DocOrWidget> documentList() const;
Q_SIGNALS:
/**
......@@ -136,7 +132,7 @@ private:
std::vector<int> documentTabIndexes() const;
bool m_isActive = false;
KTextEditor::Document *m_beingAdded = nullptr;
DocOrWidget m_beingAdded = static_cast<QWidget *>(nullptr);
/**
* limit of number of tabs we should keep
......@@ -164,7 +160,7 @@ private:
* simple 64-bit counter, worst thing that can happen on 64-bit wraparound
* is a bit strange tab replacement a few times
*/
std::unordered_map<KTextEditor::Document *, std::pair<quint64, bool>> m_docToLruCounterAndHasTab;
std::unordered_map<DocOrWidget, std::pair<quint64, bool>> m_docToLruCounterAndHasTab;
QPoint dragStartPos;
QPoint dragHotspotPos;
......
......@@ -836,23 +836,19 @@ void KateViewManager::activateView(KTextEditor::View *view)
}
}
KTextEditor::View *KateViewManager::activateView(KTextEditor::Document *d)
KTextEditor::View *KateViewManager::activateView(DocOrWidget docOrWidget)
{
// ensure we disable the current active view if we have none
if (!d) {
activateView(static_cast<KTextEditor::View *>(nullptr));
return activeView();
}
// activate existing view if possible
auto activeSpace = activeViewSpace();
if (activeSpace->showView(d)) {
if (activeSpace->showView(docOrWidget)) {
// This will be null if currentView is not a KTE::View
activateView(activeSpace->currentView());
return activeView();
}
// create new view otherwise
createView(d);
Q_ASSERT(docOrWidget.doc()); // TODO waqar: check
createView(docOrWidget.doc());
return activeView();
}
......@@ -1297,7 +1293,12 @@ void KateViewManager::removeViewSpace(KateViewSpace *viewspace)
// add the known documents to the current view space to not loose tab buttons
auto avs = activeViewSpace();
for (auto doc : documentList) {
avs->registerDocument(doc);
// TODO: unify handling
if (doc.doc()) {
avs->registerDocument(doc.doc());
} else if (doc.widget()) {
avs->addWidgetAsTab(doc.widget());
}
}
// find the view that is now active.
......
......@@ -9,9 +9,11 @@
#ifndef __KATE_VIEWMANAGER_H__
#define __KATE_VIEWMANAGER_H__
#include "doc_or_widget.h"
#include "katedocmanager.h"
#include "kateprivate_export.h"
#include "katesplitter.h"
#include "kateviewspace.h"
#include <QList>
#include <QPointer>
......@@ -211,7 +213,7 @@ public Q_SLOTS:
* activate view for given document
* @param doc document to activate view for
*/
KTextEditor::View *activateView(KTextEditor::Document *doc);
KTextEditor::View *activateView(DocOrWidget docOrWidget);
/** Splits the active viewspace horizontally */
void slotSplitViewSpaceHoriz()
......
......@@ -14,6 +14,7 @@
#include "katefileactions.h"
#include "katemainwindow.h"
#include "katesessionmanager.h"
#include "katetabbar.h"
#include "kateupdatedisabler.h"
#include "kateurlbar.h"
#include "kateviewmanager.h"
......@@ -389,34 +390,43 @@ void KateViewSpace::removeView(KTextEditor::View *v)
}
}
bool KateViewSpace::showView(KTextEditor::Document *document)
bool KateViewSpace::showView(DocOrWidget docOrWidget)
{
/**
* nothing can be done if we have no view ready here
*/
auto it = m_docToView.find(document);
auto it = m_docToView.find(docOrWidget.doc());
if (it == m_docToView.end()) {
return false;
if (!docOrWidget.widget()) {
return false;
} else if (docOrWidget.widget() && !m_registeredDocuments.contains(docOrWidget)) {
qWarning() << "Unexpected unregistred widget" << docOrWidget.widget() << ", please add it first";
return false;
}
}
/**
* update mru list order
*/
const int index = m_registeredDocuments.lastIndexOf(document);
const int index = m_registeredDocuments.lastIndexOf(docOrWidget);
// move view to end of list
if (index < 0) {
return false;
}
// move view to end of list
m_registeredDocuments.removeAt(index);
m_registeredDocuments.append(document);
m_registeredDocuments.append(docOrWidget);
/**
* show the wanted view
*/
KTextEditor::View *kv = it->second;
stack->setCurrentWidget(kv);
kv->show();
if (it != m_docToView.end()) {
KTextEditor::View *kv = it->second;
stack->setCurrentWidget(kv);
kv->show();
} else {
stack->setCurrentWidget(m_registeredDocuments.last().widget());
}
/**
* we need to avoid that below's index changes will mess with current view
......@@ -426,7 +436,7 @@ bool KateViewSpace::showView(KTextEditor::Document *document)
/**
* follow current view
*/
m_tabBar->setCurrentDocument(document);
m_tabBar->setCurrentDocument(docOrWidget);
// track tab changes again
connect(m_tabBar, &KateTabBar::currentChanged, this, &KateViewSpace::changeView);
......@@ -444,21 +454,10 @@ void KateViewSpace::changeView(int idx)
m_viewManager->setActiveSpace(this);
}
const auto docOrWidget = m_tabBar->tabData(idx).value<DocOrWidget>();
KTextEditor::Document *doc = docOrWidget.doc();
if (!doc) {
auto w = docOrWidget.widget();
if (!w) {
// can happen during widget creation if no view is there initially
return;
}
stack->setCurrentWidget(w);
m_viewManager->activateView(static_cast<KTextEditor::Document *>(nullptr));
return;
}
const auto docOrWidget = m_tabBar->tabDocument(idx);
// tell the view manager to show the view
m_viewManager->activateView(doc);
m_viewManager->activateView(docOrWidget);
}
KTextEditor::View *KateViewSpace::currentView()
......@@ -792,7 +791,7 @@ void KateViewSpace::addWidgetAsTab(QWidget *widget)
// disconnect changeView, we are just adding the widget here
// and don't want any unnecessary viewChanged signals
disconnect(m_tabBar, &KateTabBar::currentChanged, this, &KateViewSpace::changeView);
m_tabBar->setCurrentWidget(widget);
m_tabBar->setCurrentDocument(widget);
connect(m_tabBar, &KateTabBar::currentChanged, this, &KateViewSpace::changeView);
stack->setCurrentWidget(widget);
m_registeredDocuments.append(widget);
......@@ -949,9 +948,9 @@ void KateViewSpace::showContextMenu(int idx, const QPoint &globalPos)
return; // the welcome screen is open
}
auto *doc = m_tabBar->tabDocument(idx);
auto docOrWidget = m_tabBar->tabDocument(idx);
auto activeDocument = activeView->document(); // used for compareUsing which is used with another
if (!doc) {
if (!docOrWidget.doc()) {
// This tab is holding some other widget
// Show only "close tab" for now
// maybe later allow adding context menu entries from the widgets
......@@ -967,6 +966,7 @@ void KateViewSpace::showContextMenu(int idx, const QPoint &globalPos)
}
return;
}
auto *doc = docOrWidget.doc();
auto addActionFromCollection = [this](QMenu *menu, const char *action_name) {
QAction *action = m_viewManager->mainWindow()->action(action_name);
......@@ -1088,7 +1088,11 @@ void KateViewSpace::saveConfig(KConfigBase *config, int myIndex, const QString &
std::vector<KTextEditor::View *> views;
QStringList lruList;
const auto docList = documentList();
for (KTextEditor::Document *doc : docList) {
for (DocOrWidget docOrWidget : docList) {
if (docOrWidget.widget()) {
continue;
}
auto doc = docOrWidget.doc();
lruList << doc->url().toString();
auto it = m_docToView.find(doc);
if (it != m_docToView.end()) {
......
......@@ -13,6 +13,7 @@
#include <ktexteditor/modificationinterface.h>
#include <ktexteditor/view.h>
#include "doc_or_widget.h"
#include "kateprivate_export.h"
#include "katetabbar.h"
......@@ -24,38 +25,6 @@ class QStackedWidget;
class QToolButton;