1
mirror of https://invent.kde.org/network/falkon.git synced 2024-12-20 10:46:35 +01:00

TabManager: Support moving tabs between windows by drag & drop.

This commit is contained in:
Razi Alavizadeh 2017-10-03 16:24:43 +03:30
parent bad3a76a2e
commit 48c409fa76
3 changed files with 191 additions and 20 deletions

View File

@ -31,6 +31,7 @@
#include "tldextractor/tldextractor.h"
#include "tabmanagerdelegate.h"
#include "tabcontextmenu.h"
#include "tabbar.h"
#include <QDesktopWidget>
#include <QDialogButtonBox>
@ -38,6 +39,7 @@
#include <QDialog>
#include <QTimer>
#include <QLabel>
#include <QMimeData>
TLDExtractor* TabManagerWidget::s_tldExtractor = 0;
@ -59,6 +61,7 @@ TabManagerWidget::TabManagerWidget(BrowserWindow* mainClass, QWidget* parent, bo
}
ui->setupUi(this);
ui->treeWidget->setSelectionMode(QTreeWidget::SingleSelection);
ui->treeWidget->setUniformRowHeights(true);
ui->treeWidget->setColumnCount(2);
ui->treeWidget->header()->hide();
@ -85,6 +88,7 @@ TabManagerWidget::TabManagerWidget(BrowserWindow* mainClass, QWidget* parent, bo
connect(ui->filterBar, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(onItemActivated(QTreeWidgetItem*,int)));
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
connect(ui->treeWidget, SIGNAL(requestRefreshTree()), this, SLOT(delayedRefreshTree()));
}
TabManagerWidget::~TabManagerWidget()
@ -178,6 +182,8 @@ void TabManagerWidget::refreshTree()
}
ui->treeWidget->clear();
ui->treeWidget->setEnableDragTabs(m_groupType == GroupByWindow);
QTreeWidgetItem* currentTabItem = nullptr;
if (m_groupType == GroupByHost) {
@ -527,18 +533,8 @@ void TabManagerWidget::closeSelectedTabs(const QHash<BrowserWindow*, WebTab*> &t
}
}
void TabManagerWidget::detachSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
static void detachTabsTo(BrowserWindow* targetWindow, const QHash<BrowserWindow*, WebTab*> &tabsHash)
{
// TODO: use TabWidget::detachTab()
if (tabsHash.isEmpty() ||
(tabsHash.uniqueKeys().size() == 1 &&
tabsHash.size() == tabsHash.keys().at(0)->tabWidget()->count())) {
return;
}
BrowserWindow* newWindow = mApp->createWindow(Qz::BW_OtherRestoredWindow);
newWindow->move(mApp->desktop()->availableGeometry(this).topLeft() + QPoint(30, 30));
const QList<BrowserWindow*> &windows = tabsHash.uniqueKeys();
foreach (BrowserWindow* mainWindow, windows) {
const QList<WebTab*> &tabs = tabsHash.values(mainWindow);
@ -550,11 +546,25 @@ void TabManagerWidget::detachSelectedTabs(const QHash<BrowserWindow*, WebTab*> &
mainWindow = 0;
}
newWindow->tabWidget()->addView(webTab);
targetWindow->tabWidget()->addView(webTab);
}
}
}
void TabManagerWidget::detachSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
{
if (tabsHash.isEmpty() ||
(tabsHash.uniqueKeys().size() == 1 &&
tabsHash.size() == tabsHash.keys().at(0)->tabWidget()->count())) {
return;
}
BrowserWindow* newWindow = mApp->createWindow(Qz::BW_OtherRestoredWindow);
newWindow->move(mApp->desktop()->availableGeometry(this).topLeft() + QPoint(30, 30));
detachTabsTo(newWindow, tabsHash);
}
bool TabManagerWidget::bookmarkSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
{
QDialog* dialog = new QDialog(getWindow(), Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint);
@ -624,7 +634,7 @@ QTreeWidgetItem* TabManagerWidget::groupByDomainName(bool useHostName)
QString domain = domainFromUrl(webTab->url(), useHostName);
if (!tabsGroupedByDomain.contains(domain)) {
TabItem* groupItem = new TabItem(ui->treeWidget, 0, false);
TabItem* groupItem = new TabItem(ui->treeWidget, false, false, 0, false);
groupItem->setTitle(domain);
groupItem->setIsActiveOrCaption(true);
@ -633,7 +643,7 @@ QTreeWidgetItem* TabManagerWidget::groupByDomainName(bool useHostName)
QTreeWidgetItem* groupItem = tabsGroupedByDomain.value(domain);
TabItem* tabItem = new TabItem(ui->treeWidget, groupItem);
TabItem* tabItem = new TabItem(ui->treeWidget, false, true, groupItem);
tabItem->setBrowserWindow(mainWin);
tabItem->setWebTab(webTab);
@ -673,7 +683,7 @@ QTreeWidgetItem* TabManagerWidget::groupByWindow()
for (int win = 0; win < windows.count(); ++win) {
BrowserWindow* mainWin = windows.at(win);
TabItem* winItem = new TabItem(ui->treeWidget);
TabItem* winItem = new TabItem(ui->treeWidget, true, false);
winItem->setBrowserWindow(mainWin);
winItem->setText(0, tr("Window %1").arg(QString::number(win + 1)));
winItem->setToolTip(0, tr("Double click to switch"));
@ -687,7 +697,7 @@ QTreeWidgetItem* TabManagerWidget::groupByWindow()
m_webPage = 0;
continue;
}
TabItem* tabItem = new TabItem(ui->treeWidget, winItem);
TabItem* tabItem = new TabItem(ui->treeWidget, true, true, winItem);
tabItem->setBrowserWindow(mainWin);
tabItem->setWebTab(webTab);
@ -716,14 +726,29 @@ BrowserWindow* TabManagerWidget::getWindow()
}
}
TabItem::TabItem(QTreeWidget* treeWidget, QTreeWidgetItem* parent, bool addToTree)
TabItem::TabItem(QTreeWidget* treeWidget, bool supportDrag, bool isTab, QTreeWidgetItem* parent, bool addToTree)
: QObject()
, QTreeWidgetItem(addToTree ? (parent ? parent : treeWidget->invisibleRootItem()) : 0, 1)
, m_treeWidget(treeWidget)
, m_window(0)
, m_webTab(0)
, m_isTab(isTab)
{
setFlags(flags() | (parent ? Qt::ItemIsUserCheckable : Qt::ItemIsUserCheckable | Qt::ItemIsTristate));
Qt::ItemFlags flgs = flags() | (parent ? Qt::ItemIsUserCheckable : Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
if (supportDrag) {
if (isTab) {
flgs |= Qt::ItemIsDragEnabled | Qt::ItemNeverHasChildren;
flgs &= ~Qt::ItemIsDropEnabled;
}
else {
flgs |= Qt::ItemIsDropEnabled;
flgs &= ~Qt::ItemIsDragEnabled;
}
}
setFlags(flgs);
setCheckState(0, Qt::Unchecked);
}
@ -807,3 +832,122 @@ void TabItem::setIsSavedTab(bool yes)
{
setData(0, SavedRole, yes ? QVariant(true) : QVariant());
}
bool TabItem::isTab() const
{
return m_isTab;
}
TabTreeWidget::TabTreeWidget(QWidget *parent)
: QTreeWidget(parent)
{
invisibleRootItem()->setFlags(invisibleRootItem()->flags() & ~Qt::ItemIsDropEnabled);
}
Qt::DropActions TabTreeWidget::supportedDropActions() const
{
return Qt::MoveAction | Qt::CopyAction;
}
#define MIMETYPE QLatin1String("application/qupzilla.tabs")
QStringList TabTreeWidget::mimeTypes() const
{
QStringList types;
types.append(MIMETYPE);
return types;
}
QMimeData *TabTreeWidget::mimeData(const QList<QTreeWidgetItem*> items) const
{
QMimeData* mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
if (items.size() > 0) {
TabItem* tabItem = static_cast<TabItem*>(items.at(0));
if (!tabItem || !tabItem->isTab())
return 0;
stream << (quintptr) tabItem->window() << (quintptr) tabItem->webTab();
mimeData->setData(MIMETYPE, encodedData);
return mimeData;
}
return 0;
}
bool TabTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
if (action == Qt::IgnoreAction) {
return true;
}
TabItem* parentItem = static_cast<TabItem*>(parent);
if (!data->hasFormat(MIMETYPE) || !parentItem) {
return false;
}
Q_ASSERT(!parentItem->isTab());
BrowserWindow* targetWindow = parentItem->window();
QByteArray encodedData = data->data(MIMETYPE);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
if (!stream.atEnd()) {
quintptr webTabPtr;
quintptr windowPtr;
stream >> windowPtr >> webTabPtr;
WebTab* webTab = (WebTab*) webTabPtr;
BrowserWindow* window = (BrowserWindow*) windowPtr;
if (window == targetWindow) {
if (index > 0 && webTab->tabIndex() < index)
--index;
if (webTab->isPinned() && index >= targetWindow->tabWidget()->pinnedTabsCount())
index = targetWindow->tabWidget()->pinnedTabsCount() - 1;
if (!webTab->isPinned() && index < targetWindow->tabWidget()->pinnedTabsCount())
index = targetWindow->tabWidget()->pinnedTabsCount();
if (index != webTab->tabIndex()) {
targetWindow->tabWidget()->tabBar()->moveTab(webTab->tabIndex(), index);
if (!webTab->isCurrentTab())
emit requestRefreshTree();
}
else {
return false;
}
}
else if (!webTab->isPinned()) {
QHash<BrowserWindow*, WebTab*> tabsHash;
tabsHash.insert(window, webTab);
detachTabsTo(targetWindow, tabsHash);
if (index < targetWindow->tabWidget()->pinnedTabsCount())
index = targetWindow->tabWidget()->pinnedTabsCount();
targetWindow->tabWidget()->tabBar()->moveTab(webTab->tabIndex(), index);
}
}
return true;
}
void TabTreeWidget::setEnableDragTabs(bool enable)
{
setDragEnabled(enable);
setAcceptDrops(enable);
viewport()->setAcceptDrops(enable);
setDropIndicatorShown(enable);
}

View File

@ -35,6 +35,25 @@ class WebTab;
class WebView;
class TLDExtractor;
class TabTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
TabTreeWidget(QWidget* parent = 0);
Qt::DropActions supportedDropActions() const;
QStringList mimeTypes() const;
QMimeData* mimeData(const QList<QTreeWidgetItem*> items) const;
bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action);
void setEnableDragTabs(bool enable);
signals:
void requestRefreshTree();
};
class TabManagerWidget : public QWidget
{
Q_OBJECT
@ -107,7 +126,7 @@ public:
SavedRole = Qt::UserRole + 2
};
TabItem(QTreeWidget* treeWidget, QTreeWidgetItem* parent = 0, bool addToTree = true);
TabItem(QTreeWidget* treeWidget, bool supportDrag = true, bool isTab = true, QTreeWidgetItem* parent = 0, bool addToTree = true);
BrowserWindow* window() const;
void setBrowserWindow(BrowserWindow* window);
@ -115,6 +134,8 @@ public:
WebTab* webTab() const;
void setWebTab(WebTab* webTab);
bool isTab() const;
public slots:
void updateIcon();
void setTitle(const QString& title);
@ -125,6 +146,7 @@ private:
QTreeWidget* m_treeWidget;
BrowserWindow* m_window;
WebTab* m_webTab;
bool m_isTab;
};
#endif // TABMANAGERWIDGET_H

View File

@ -33,7 +33,7 @@
<widget class="LineEdit" name="filterBar"/>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<widget class="TabTreeWidget" name="treeWidget">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
@ -52,6 +52,11 @@
<extends>QLineEdit</extends>
<header>lineedit.h</header>
</customwidget>
<customwidget>
<class>TabTreeWidget</class>
<extends>QTreeWidget</extends>
<header>tabmanagerwidget.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>treeWidget</tabstop>