mirror of
https://invent.kde.org/network/falkon.git
synced 2024-09-21 09:42:10 +02:00
Add TabTreeModel
This model orders tabs in "Tree Style Tabs" fashion.
This commit is contained in:
parent
57b605e4e0
commit
e57fb65c95
|
@ -18,6 +18,7 @@
|
||||||
#include "tabmodeltest.h"
|
#include "tabmodeltest.h"
|
||||||
#include "autotests.h"
|
#include "autotests.h"
|
||||||
#include "tabmodel.h"
|
#include "tabmodel.h"
|
||||||
|
#include "tabtreemodel.h"
|
||||||
#include "webtab.h"
|
#include "webtab.h"
|
||||||
#include "tabwidget.h"
|
#include "tabwidget.h"
|
||||||
#include "tabbedwebview.h"
|
#include "tabbedwebview.h"
|
||||||
|
@ -118,5 +119,102 @@ void TabModelTest::dataTest()
|
||||||
|
|
||||||
delete w;
|
delete w;
|
||||||
}
|
}
|
||||||
|
void TabModelTest::treeModelTest()
|
||||||
|
{
|
||||||
|
BrowserWindow *w = mApp->createWindow(Qz::BW_NewWindow);
|
||||||
|
|
||||||
|
TabModel sourceModel(w);
|
||||||
|
TabTreeModel model;
|
||||||
|
model.setSourceModel(&sourceModel);
|
||||||
|
ModelTest modelTest(&model);
|
||||||
|
|
||||||
|
w->tabWidget()->addView(QUrl());
|
||||||
|
w->tabWidget()->addView(QUrl());
|
||||||
|
w->tabWidget()->addView(QUrl());
|
||||||
|
w->tabWidget()->addView(QUrl());
|
||||||
|
w->tabWidget()->addView(QUrl());
|
||||||
|
|
||||||
|
QTRY_COMPARE(model.rowCount(QModelIndex()), 6);
|
||||||
|
|
||||||
|
WebTab *tab1 = w->weView(0)->webTab();
|
||||||
|
WebTab *tab2 = w->weView(1)->webTab();
|
||||||
|
WebTab *tab3 = w->weView(2)->webTab();
|
||||||
|
WebTab *tab4 = w->weView(3)->webTab();
|
||||||
|
WebTab *tab5 = w->weView(4)->webTab();
|
||||||
|
WebTab *tab6 = w->weView(5)->webTab();
|
||||||
|
|
||||||
|
QCOMPARE(model.index(0, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab1);
|
||||||
|
QCOMPARE(model.index(1, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(2, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
QCOMPARE(model.index(3, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab4);
|
||||||
|
QCOMPARE(model.index(4, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(5, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
|
||||||
|
QPersistentModelIndex tab1index = model.index(0, 0);
|
||||||
|
QPersistentModelIndex tab2index = model.index(1, 0);
|
||||||
|
QPersistentModelIndex tab3index = model.index(2, 0);
|
||||||
|
QPersistentModelIndex tab4index = model.index(3, 0);
|
||||||
|
QPersistentModelIndex tab5index = model.index(4, 0);
|
||||||
|
QPersistentModelIndex tab6index = model.index(5, 0);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 0);
|
||||||
|
tab1->addChildTab(tab2);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 1);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
|
||||||
|
tab1->addChildTab(tab3);
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 2);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(1, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
|
||||||
|
tab1->addChildTab(tab4, 1);
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 3);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(1, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab4);
|
||||||
|
QCOMPARE(model.index(2, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
|
||||||
|
tab4->addChildTab(tab5);
|
||||||
|
tab4->addChildTab(tab6);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab4index), 2);
|
||||||
|
QCOMPARE(model.index(0, 0, tab4index).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(1, 0, tab4index).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
|
||||||
|
w->tabWidget()->closeTab(tab4->tabIndex());
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 4);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(1, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(2, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
QCOMPARE(model.index(3, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
|
||||||
|
tab1->addChildTab(tab3, 0);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 4);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
QCOMPARE(model.index(1, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(2, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(3, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
|
||||||
|
tab2->setParentTab(nullptr);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(tab1index), 3);
|
||||||
|
QCOMPARE(model.index(0, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(0, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
QCOMPARE(model.index(1, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(2, 0, tab1index).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
|
||||||
|
w->tabWidget()->closeTab(tab1->tabIndex());
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(QModelIndex()), 4);
|
||||||
|
QCOMPARE(model.index(0, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab2);
|
||||||
|
QCOMPARE(model.index(1, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab3);
|
||||||
|
QCOMPARE(model.index(2, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab5);
|
||||||
|
QCOMPARE(model.index(3, 0).data(TabModel::WebTabRole).value<WebTab*>(), tab6);
|
||||||
|
|
||||||
|
QTest::qWait(1);
|
||||||
|
delete w;
|
||||||
|
}
|
||||||
|
|
||||||
FALKONTEST_MAIN(TabModelTest)
|
FALKONTEST_MAIN(TabModelTest)
|
||||||
|
|
|
@ -29,4 +29,5 @@ private slots:
|
||||||
|
|
||||||
void basicTest();
|
void basicTest();
|
||||||
void dataTest();
|
void dataTest();
|
||||||
|
void treeModelTest();
|
||||||
};
|
};
|
||||||
|
|
|
@ -180,6 +180,7 @@ set(SRCS ${SRCS}
|
||||||
tabwidget/tabbar.cpp
|
tabwidget/tabbar.cpp
|
||||||
tabwidget/tabicon.cpp
|
tabwidget/tabicon.cpp
|
||||||
tabwidget/tabmodel.cpp
|
tabwidget/tabmodel.cpp
|
||||||
|
tabwidget/tabtreemodel.cpp
|
||||||
tabwidget/tabstackedwidget.cpp
|
tabwidget/tabstackedwidget.cpp
|
||||||
tabwidget/tabwidget.cpp
|
tabwidget/tabwidget.cpp
|
||||||
tabwidget/tabcontextmenu.cpp
|
tabwidget/tabcontextmenu.cpp
|
||||||
|
|
390
src/lib/tabwidget/tabtreemodel.cpp
Normal file
390
src/lib/tabwidget/tabtreemodel.cpp
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
/* ============================================================
|
||||||
|
* Falkon - Qt web browser
|
||||||
|
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#include "tabtreemodel.h"
|
||||||
|
#include "tabmodel.h"
|
||||||
|
#include "webtab.h"
|
||||||
|
|
||||||
|
#include <QMimeData>
|
||||||
|
|
||||||
|
class TabTreeModelItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TabTreeModelItem(WebTab *tab = nullptr, const QModelIndex &index = QModelIndex());
|
||||||
|
~TabTreeModelItem();
|
||||||
|
|
||||||
|
bool isRoot() const;
|
||||||
|
void setParent(TabTreeModelItem *item);
|
||||||
|
void addChild(TabTreeModelItem *item, int index = -1);
|
||||||
|
|
||||||
|
WebTab *tab = nullptr;
|
||||||
|
TabTreeModelItem *parent = nullptr;
|
||||||
|
QVector<TabTreeModelItem*> children;
|
||||||
|
QPersistentModelIndex sourceIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
TabTreeModelItem::TabTreeModelItem(WebTab *tab, const QModelIndex &index)
|
||||||
|
: tab(tab)
|
||||||
|
, sourceIndex(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TabTreeModelItem::~TabTreeModelItem()
|
||||||
|
{
|
||||||
|
for (TabTreeModelItem *child : qAsConst(children)) {
|
||||||
|
delete child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabTreeModelItem::isRoot() const
|
||||||
|
{
|
||||||
|
return !tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModelItem::setParent(TabTreeModelItem *item)
|
||||||
|
{
|
||||||
|
if (parent == item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
parent->children.removeOne(this);
|
||||||
|
}
|
||||||
|
parent = item;
|
||||||
|
if (parent) {
|
||||||
|
parent->children.append(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModelItem::addChild(TabTreeModelItem *item, int index)
|
||||||
|
{
|
||||||
|
item->setParent(nullptr);
|
||||||
|
item->parent = this;
|
||||||
|
if (index < 0 || index > children.size()) {
|
||||||
|
children.append(item);
|
||||||
|
} else {
|
||||||
|
children.insert(index, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TabTreeModel::TabTreeModel(QObject *parent)
|
||||||
|
: QAbstractProxyModel(parent)
|
||||||
|
{
|
||||||
|
connect(this, &QAbstractProxyModel::sourceModelChanged, this, &TabTreeModel::init);
|
||||||
|
}
|
||||||
|
|
||||||
|
TabTreeModel::~TabTreeModel()
|
||||||
|
{
|
||||||
|
delete m_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::tabIndex(WebTab *tab) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *item = m_items.value(tab);
|
||||||
|
if (!item) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
return createIndex(item->parent->children.indexOf(item), 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebTab *TabTreeModel::tab(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = item(index);
|
||||||
|
return it ? it->tab : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags TabTreeModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return Qt::ItemIsDropEnabled;
|
||||||
|
}
|
||||||
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TabTreeModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
return sourceModel()->data(mapToSource(index), role);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TabTreeModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = item(parent);
|
||||||
|
if (!it) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return it->children.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TabTreeModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.column() > 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabTreeModel::hasChildren(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = item(parent);
|
||||||
|
if (!it) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !it->children.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::parent(const QModelIndex &child) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = item(child);
|
||||||
|
if (!it) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
return index(it->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (!hasIndex(row, column, parent)) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
TabTreeModelItem *parentItem = item(parent);
|
||||||
|
return createIndex(row, column, parentItem->children.at(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::mapFromSource(const QModelIndex &sourceIndex) const
|
||||||
|
{
|
||||||
|
return tabIndex(sourceIndex.data(TabModel::WebTabRole).value<WebTab*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::mapToSource(const QModelIndex &proxyIndex) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = item(proxyIndex);
|
||||||
|
if (!it) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
return it->sourceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
if (action == Qt::IgnoreAction) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString mimeType = mimeTypes().at(0);
|
||||||
|
|
||||||
|
if (!data->hasFormat(mimeType) || column > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray encodedData = data->data(mimeType);
|
||||||
|
QDataStream stream(&encodedData, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QVector<WebTab*> tabs;
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
int index;
|
||||||
|
stream >> index;
|
||||||
|
WebTab *tab = sourceModel()->index(index, 0).data(TabModel::WebTabRole).value<WebTab*>();
|
||||||
|
if (tab) {
|
||||||
|
tabs.append(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabs.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only support moving one tab
|
||||||
|
WebTab *tab = tabs.at(0);
|
||||||
|
|
||||||
|
TabTreeModelItem *it = m_items.value(tab);
|
||||||
|
TabTreeModelItem *parentItem = item(parent);
|
||||||
|
if (!it || !parentItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parentItem->tab) {
|
||||||
|
tab->setParentTab(nullptr);
|
||||||
|
if (row < 0) {
|
||||||
|
row = m_root->children.count();
|
||||||
|
}
|
||||||
|
const QModelIndex fromIdx = index(it);
|
||||||
|
const int childPos = row > fromIdx.row() ? row - 1 : row;
|
||||||
|
if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), QModelIndex(), row)) {
|
||||||
|
qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << "root" << row;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_root->addChild(it, childPos);
|
||||||
|
endMoveRows();
|
||||||
|
} else {
|
||||||
|
parentItem->tab->addChildTab(tab, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::init()
|
||||||
|
{
|
||||||
|
delete m_root;
|
||||||
|
m_items.clear();
|
||||||
|
|
||||||
|
m_root = new TabTreeModelItem;
|
||||||
|
|
||||||
|
for (int i = 0; i < sourceModel()->rowCount(); ++i) {
|
||||||
|
const QModelIndex index = sourceModel()->index(i, 0);
|
||||||
|
WebTab *tab = index.data(TabModel::WebTabRole).value<WebTab*>();
|
||||||
|
if (tab && !tab->parentTab()) {
|
||||||
|
TabTreeModelItem *item = new TabTreeModelItem(tab, index);
|
||||||
|
m_items[tab] = item;
|
||||||
|
m_root->addChild(createItems(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TabTreeModelItem *item : qAsConst(m_items)) {
|
||||||
|
connectTab(item->tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(sourceModel(), &QAbstractItemModel::dataChanged, this, &TabTreeModel::sourceDataChanged);
|
||||||
|
connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &TabTreeModel::sourceRowsInserted);
|
||||||
|
connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &TabTreeModel::sourceRowsAboutToBeRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TabTreeModel::index(TabTreeModelItem *item) const
|
||||||
|
{
|
||||||
|
if (!item || item == m_root) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
return createIndex(item->parent->children.indexOf(item), 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
TabTreeModelItem *TabTreeModel::item(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
TabTreeModelItem *it = static_cast<TabTreeModelItem*>(index.internalPointer());
|
||||||
|
return it ? it : m_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
TabTreeModelItem *TabTreeModel::createItems(TabTreeModelItem *root)
|
||||||
|
{
|
||||||
|
const auto children = root->tab->childTabs();
|
||||||
|
for (WebTab *child : children) {
|
||||||
|
const QModelIndex index = sourceModel()->index(child->tabIndex(), 0);
|
||||||
|
TabTreeModelItem *item = new TabTreeModelItem(child, index);
|
||||||
|
m_items[child] = item;
|
||||||
|
root->addChild(createItems(item));
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
|
||||||
|
{
|
||||||
|
emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
insertIndex(sourceModel()->index(i, 0, parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
removeIndex(sourceModel()->index(i, 0, parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::insertIndex(const QModelIndex &sourceIndex)
|
||||||
|
{
|
||||||
|
WebTab *tab = sourceIndex.data(TabModel::WebTabRole).value<WebTab*>();
|
||||||
|
if (!tab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TabTreeModelItem *parent = m_items.value(tab->parentTab());
|
||||||
|
if (!parent) {
|
||||||
|
parent = m_root;
|
||||||
|
}
|
||||||
|
TabTreeModelItem *item = new TabTreeModelItem(tab, sourceIndex);
|
||||||
|
|
||||||
|
const int idx = parent->children.count();
|
||||||
|
beginInsertRows(tabIndex(tab->parentTab()), idx, idx);
|
||||||
|
m_items[tab] = item;
|
||||||
|
parent->addChild(item);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
connectTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::removeIndex(const QModelIndex &sourceIndex)
|
||||||
|
{
|
||||||
|
WebTab *tab = sourceIndex.data(TabModel::WebTabRole).value<WebTab*>();
|
||||||
|
if (!tab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TabTreeModelItem *item = m_items.value(tab);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QModelIndex index = mapFromSource(sourceIndex);
|
||||||
|
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||||
|
item->setParent(nullptr);
|
||||||
|
Q_ASSERT(item->children.isEmpty());
|
||||||
|
delete item;
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
tab->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabTreeModel::connectTab(WebTab *tab)
|
||||||
|
{
|
||||||
|
TabTreeModelItem *item = m_items.value(tab);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
|
||||||
|
connect(tab, &WebTab::parentTabChanged, this, [=](WebTab *parent) {
|
||||||
|
// Handle only move to root, everything else is done in childTabAdded
|
||||||
|
if (item->parent == m_root || parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int pos = m_root->children.count();
|
||||||
|
// Move it to the same spot as old parent
|
||||||
|
if (item->parent->parent == m_root) {
|
||||||
|
pos = m_root->children.indexOf(item->parent);
|
||||||
|
}
|
||||||
|
const QModelIndex fromIdx = index(item);
|
||||||
|
if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), QModelIndex(), pos)) {
|
||||||
|
qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << "root" << pos;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_root->addChild(item, pos);
|
||||||
|
endMoveRows();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(tab, &WebTab::childTabAdded, this, [=](WebTab *child, int pos) {
|
||||||
|
TabTreeModelItem *from = m_items.value(child);
|
||||||
|
if (!from) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QModelIndex fromIdx = index(from);
|
||||||
|
const QModelIndex toIdx = index(item);
|
||||||
|
const int childPos = fromIdx.parent() == toIdx && pos > fromIdx.row() ? pos - 1 : pos;
|
||||||
|
if (!beginMoveRows(fromIdx.parent(), fromIdx.row(), fromIdx.row(), toIdx, pos)) {
|
||||||
|
qWarning() << "Invalid beginMoveRows" << fromIdx.parent() << fromIdx.row() << toIdx << pos;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item->addChild(from, childPos);
|
||||||
|
endMoveRows();
|
||||||
|
});
|
||||||
|
}
|
67
src/lib/tabwidget/tabtreemodel.h
Normal file
67
src/lib/tabwidget/tabtreemodel.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/* ============================================================
|
||||||
|
* Falkon - Qt web browser
|
||||||
|
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* ============================================================ */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractProxyModel>
|
||||||
|
|
||||||
|
#include "qzcommon.h"
|
||||||
|
|
||||||
|
class WebTab;
|
||||||
|
class TabTreeModelItem;
|
||||||
|
|
||||||
|
class FALKON_EXPORT TabTreeModel : public QAbstractProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TabTreeModel(QObject *parent = nullptr);
|
||||||
|
~TabTreeModel();
|
||||||
|
|
||||||
|
QModelIndex tabIndex(WebTab *tab) const;
|
||||||
|
WebTab *tab(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
|
bool hasChildren(const QModelIndex &parent) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
|
||||||
|
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
|
||||||
|
|
||||||
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
QModelIndex index(TabTreeModelItem *item) const;
|
||||||
|
TabTreeModelItem *item(const QModelIndex &index) const;
|
||||||
|
TabTreeModelItem *createItems(TabTreeModelItem *root);
|
||||||
|
|
||||||
|
void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
|
||||||
|
void sourceRowsInserted(const QModelIndex &parent, int start, int end);
|
||||||
|
void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
|
||||||
|
|
||||||
|
void insertIndex(const QModelIndex &sourceIndex);
|
||||||
|
void removeIndex(const QModelIndex &sourceIndex);
|
||||||
|
void connectTab(WebTab *tab);
|
||||||
|
|
||||||
|
TabTreeModelItem *m_root = nullptr;
|
||||||
|
QHash<WebTab*, TabTreeModelItem*> m_items;
|
||||||
|
};
|
|
@ -444,12 +444,17 @@ void WebTab::setParentTab(WebTab *tab)
|
||||||
|
|
||||||
void WebTab::addChildTab(WebTab *tab, int index)
|
void WebTab::addChildTab(WebTab *tab, int index)
|
||||||
{
|
{
|
||||||
if (tab->parentTab()) {
|
|
||||||
tab->setParentTab(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
tab->m_parentTab = this;
|
tab->m_parentTab = this;
|
||||||
|
|
||||||
|
WebTab *tabParent = tab->m_parentTab;
|
||||||
|
if (tabParent) {
|
||||||
|
const int index = tabParent->m_childTabs.indexOf(tab);
|
||||||
|
if (index >= 0) {
|
||||||
|
tabParent->m_childTabs.removeAt(index);
|
||||||
|
emit tabParent->childTabRemoved(tab, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (index < 0 || index > m_childTabs.size()) {
|
if (index < 0 || index > m_childTabs.size()) {
|
||||||
m_childTabs.append(tab);
|
m_childTabs.append(tab);
|
||||||
emit childTabAdded(tab, m_childTabs.size() - 1);
|
emit childTabAdded(tab, m_childTabs.size() - 1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user