mirror of
https://github.com/epasveer/seer.git
synced 2026-08-29 16:40:42 +08:00
303 lines
8.0 KiB
C++
303 lines
8.0 KiB
C++
// SPDX-FileCopyrightText: 2021 Ernie Pasveer <epasveer@att.net>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "QDetachTabWidget.h"
|
|
#include <QtWidgets/QTabBar>
|
|
#include <QtWidgets/QMenu>
|
|
#include <QtWidgets/QHBoxLayout>
|
|
#include <QAction>
|
|
#include <QtGui/QCursor>
|
|
#include <QtGui/QIcon>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QDebug>
|
|
|
|
QDetachTabWidget::QDetachTabWidget(QWidget* parent) : QTabWidget(parent) {
|
|
|
|
tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
QObject::connect(tabBar(), &QTabBar::customContextMenuRequested, this, &QDetachTabWidget::handleShowContextMenu);
|
|
QObject::connect(this, &QTabWidget::tabCloseRequested, this, &QDetachTabWidget::handleTabClosedRequested);
|
|
}
|
|
|
|
bool QDetachTabWidget::isDetached (int tabIndex) const {
|
|
|
|
// Get the tab the user selected.
|
|
QWidget* w = widget(tabIndex);
|
|
|
|
if (w == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// If it's the 'placeholder' it is detached.
|
|
if (w->objectName() == "QDetachTabWidgetPlaceholder") {
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, it's still attached.
|
|
return false;
|
|
}
|
|
|
|
QWidget* QDetachTabWidget::tabWidget (int tabIndex) const {
|
|
|
|
if (tabIndex < 0 || tabIndex > _tabInfo.size()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return _tabInfo[tabIndex]._widget;
|
|
}
|
|
|
|
void QDetachTabWidget::detachTab (int tabIndex, bool minimized) {
|
|
|
|
// Get the tab the user selected.
|
|
QWidget* w = widget(tabIndex);
|
|
|
|
if (w == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// It can't be a 'placeholder' as it was already detached.
|
|
if (w->objectName() == "QDetachTabWidgetPlaceholder") {
|
|
return;
|
|
}
|
|
|
|
// Create a blank 'placeholder' tab.
|
|
QDetachTabWidgetPlaceholder* placeholder = new QDetachTabWidgetPlaceholder(tabIndex);
|
|
|
|
// Create an entry for the tab cache. Remember the real tab and the 'placeholder' tab. Plus the tab title.
|
|
QDetachTabInfo tabinfo(tabText(tabIndex), w, placeholder);
|
|
_tabInfo.append(tabinfo);
|
|
|
|
// Remove the real tab.
|
|
removeTab(tabIndex);
|
|
|
|
// Insert the 'placeholder' in the same position.
|
|
insertTab(tabIndex, placeholder, tabinfo._title);
|
|
|
|
// Tweak the detached tab to make sure it can't be destroyed as it is now a normal window.
|
|
w->setParent(0);
|
|
Qt::WindowFlags flags = w->windowFlags();
|
|
Qt::WindowFlags closeFlag = Qt::WindowCloseButtonHint;
|
|
flags = flags & (~closeFlag);
|
|
w->setWindowFlags(flags);
|
|
w->setWindowTitle(tabinfo._title);
|
|
w->setWindowIcon(windowIcon());
|
|
|
|
if (minimized) {
|
|
w->showMinimized();
|
|
}else{
|
|
w->showNormal();
|
|
}
|
|
|
|
// Connect the placeholder's 'reattach' signal to the slot.
|
|
QObject::connect(placeholder, &QDetachTabWidgetPlaceholder::reattach, this, &QDetachTabWidget::handleTabClosedRequested);
|
|
|
|
// Notify listeners the tab was detached.
|
|
emit tabDetached(tabIndex);
|
|
emit tabDetached(w);
|
|
}
|
|
|
|
void QDetachTabWidget::reattachTab (int tabIndex) {
|
|
|
|
// Get the tab the user selected to re-attach.
|
|
QWidget* w = widget(tabIndex);
|
|
|
|
if (w == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// It must be a 'placeholder'
|
|
if (w->objectName() != "QDetachTabWidgetPlaceholder") {
|
|
return;
|
|
}
|
|
|
|
// Look for the 'placeholder' tab in the tab cache.
|
|
// Note, we do a scan of the cache because a QTabWidget can have isMovable() enabled.
|
|
// Using 'tabindex' is not always reliable.
|
|
QList<QDetachTabInfo>::iterator it = findPlaceholderWidget(w);
|
|
if (it == _tabInfo.end()) {
|
|
return;
|
|
}
|
|
|
|
// Remove the 'placeholder' tab.
|
|
removeTab(tabIndex); delete w;
|
|
|
|
// Insert the real tab in the same position.
|
|
insertTab(tabIndex, it->_widget, it->_title);
|
|
|
|
// Save the real tab's widget before we delete the entry.
|
|
w = it->_widget;
|
|
|
|
// Delete the entry from the tab cache.
|
|
_tabInfo.erase(it);
|
|
|
|
// Notify listeners the tab was reattached.
|
|
emit tabReattached(tabIndex);
|
|
emit tabReattached(w);
|
|
}
|
|
|
|
void QDetachTabWidget::closeEvent (QCloseEvent* e) {
|
|
|
|
auto it = _tabInfo.begin();
|
|
|
|
while (it != _tabInfo.end()) {
|
|
|
|
delete it->_widget;
|
|
|
|
it = _tabInfo.erase(it); // Erase current tab and point to the next one.
|
|
}
|
|
|
|
QTabWidget::closeEvent(e);
|
|
}
|
|
|
|
void QDetachTabWidget::handleShowContextMenu (const QPoint& point) {
|
|
|
|
// Don't do anything.
|
|
if (point.isNull()) {
|
|
return;
|
|
}
|
|
|
|
// Get the tab index at the RMB click point.
|
|
int tabIndex = tabBar()->tabAt(point);
|
|
|
|
// Create the menu.
|
|
QMenu menu("Window Action", this);
|
|
|
|
QAction* detachAction = menu.addAction(tr("Detach"));
|
|
QAction* detachMinimizedAction = menu.addAction(tr("Detach Minimized"));
|
|
QAction* reattachAction = menu.addAction(tr("Reattach"));
|
|
|
|
// Enable/disable depending if it was already detached.
|
|
QWidget* w = widget(tabIndex);
|
|
if (w->objectName() == "QDetachTabWidgetPlaceholder") {
|
|
detachAction->setEnabled(false);
|
|
detachMinimizedAction->setEnabled(false);
|
|
reattachAction->setEnabled(true);
|
|
}else{
|
|
detachAction->setEnabled(true);
|
|
detachMinimizedAction->setEnabled(true);
|
|
reattachAction->setEnabled(false);
|
|
}
|
|
|
|
// Exec the menue
|
|
QAction* action = menu.exec(QCursor::pos());
|
|
|
|
//
|
|
// Handle detaching a tab.
|
|
//
|
|
if (action == detachAction) {
|
|
|
|
// Detach the tab.
|
|
detachTab(tabIndex, false);
|
|
|
|
// Set the tabwidget to the placeholder tab.
|
|
setCurrentIndex(tabIndex);
|
|
}
|
|
|
|
//
|
|
// Handle detaching a tab.
|
|
//
|
|
if (action == detachMinimizedAction) {
|
|
|
|
// Detach the tab.
|
|
detachTab(tabIndex, true);
|
|
|
|
// Set the tabwidget to the placeholder tab.
|
|
setCurrentIndex(tabIndex);
|
|
}
|
|
|
|
//
|
|
// Handle reattaching a tab.
|
|
//
|
|
if (action == reattachAction) {
|
|
|
|
// Reattach the tab.
|
|
reattachTab(tabIndex);
|
|
|
|
// Set the tabwidget to the real tab.
|
|
setCurrentIndex(tabIndex);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The QDetachTabWidget listens for 'tabCloseRequests'. Only to
|
|
// reattach the widget, if necessary. Normally, you derive from
|
|
// QDetachTabWidget and add signal/connect to do the same. I'm
|
|
// counting on mine happening first.
|
|
//
|
|
void QDetachTabWidget::handleTabClosedRequested (int tabIndex) {
|
|
|
|
// Reattach the tab.
|
|
reattachTab(tabIndex);
|
|
|
|
// Set the tabwidget to the real tab.
|
|
setCurrentIndex(tabIndex);
|
|
}
|
|
|
|
//
|
|
// Look for the real widget in the tab cache.
|
|
//
|
|
QList<QDetachTabInfo>::iterator QDetachTabWidget::findWidget (QWidget* widget) {
|
|
|
|
QList<QDetachTabInfo>::iterator it = _tabInfo.begin();
|
|
|
|
while (it != _tabInfo.end()) {
|
|
if (it->_widget == widget) {
|
|
return it;
|
|
}
|
|
|
|
it++;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
//
|
|
// Look for the 'placeholder' widget in the tab cache.
|
|
//
|
|
QList<QDetachTabInfo>::iterator QDetachTabWidget::findPlaceholderWidget (QWidget* widget) {
|
|
|
|
QList<QDetachTabInfo>::iterator it = _tabInfo.begin();
|
|
|
|
while (it != _tabInfo.end()) {
|
|
if (it->_placeholderWidget == widget) {
|
|
return it;
|
|
}
|
|
|
|
it++;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
//
|
|
// A placeholder widget that has a 'reattach' pushbutton.
|
|
//
|
|
QDetachTabWidgetPlaceholder::QDetachTabWidgetPlaceholder(int tabIndex, QWidget* parent) : QWidget(parent) {
|
|
|
|
// This the placeholder's object name. Users of this object can focus
|
|
// on this name if they need too.
|
|
setObjectName("QDetachTabWidgetPlaceholder");
|
|
|
|
// Create the 'Reattach' button and add a layout to it.
|
|
_reattachPushButton = new QPushButton("Reattach", this);
|
|
_reattachPushButton->setIcon(QIcon(":/qt-project.org/styles/commonstyle/images/right-16.png"));
|
|
_tabIndex = tabIndex;
|
|
|
|
QHBoxLayout* layout = new QHBoxLayout;
|
|
layout->addStretch();
|
|
layout->addWidget(_reattachPushButton);
|
|
layout->addStretch();
|
|
|
|
setLayout(layout);
|
|
|
|
// We'll handle the 'click' signal.
|
|
QObject::connect(_reattachPushButton, &QPushButton::clicked, this, &QDetachTabWidgetPlaceholder::handlePushButtonClicked);
|
|
}
|
|
|
|
// Handle the pushbutton 'click' by emitting the 'reattach' signal.
|
|
void QDetachTabWidgetPlaceholder::handlePushButtonClicked () {
|
|
emit reattach(_tabIndex);
|
|
}
|
|
|