384 lines
13 KiB
C++
384 lines
13 KiB
C++
/**
|
||
* @file PaiWorkspace.cpp
|
||
* @brief 该类代表一个复杂布局(支持停靠、区域拆分和页签等方式组织管理界面)的容器控件
|
||
* @date 2012-05-07
|
||
*/
|
||
#include <QApplication>
|
||
#include <QMouseEvent>
|
||
#include <QGridLayout>
|
||
#include <QRubberBand>
|
||
#include <QByteArray>
|
||
#include <QSplitter>
|
||
|
||
#include "PaiWorkspace.h"
|
||
#include "GlobalUtility.h"
|
||
#include "PaiWindow.h"
|
||
|
||
using namespace pai::gui;
|
||
|
||
namespace pai
|
||
{
|
||
namespace gui
|
||
{
|
||
/**
|
||
* @class PaiDropMask
|
||
* @brief 该类以透明蒙皮加虚框的方式指示被拖拽的页签将要落下的位置
|
||
*/
|
||
class PAI_WIDGET_EXPORT PaiDropMask : public QRubberBand
|
||
{
|
||
public:
|
||
/**
|
||
* @brief 构造函数
|
||
* @param[in] pWorkspace 父对象
|
||
*/
|
||
PaiDropMask(PaiWorkspace *pWorkspace) :
|
||
QRubberBand(QRubberBand::Rectangle, pWorkspace)
|
||
{
|
||
installEventFilter(this);
|
||
}
|
||
|
||
protected:
|
||
/**
|
||
* @brief 事件Filter
|
||
* @param[in] pObject 事件对象
|
||
* @param[in] pEvent 事件
|
||
* @return 事件运行结果
|
||
*/
|
||
bool eventFilter(QObject *pObject, QEvent *pEvent)
|
||
{
|
||
if(dynamic_cast< QInputEvent* > (pEvent) || dynamic_cast< QDropEvent* > (pEvent))
|
||
{
|
||
pEvent->ignore(); // 由父亲PaiWorkspace来接管鼠标事件和拖拽事件
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return QRubberBand::eventFilter(pObject, pEvent);
|
||
}
|
||
}
|
||
};
|
||
|
||
}
|
||
}
|
||
|
||
PaiWorkspaceExtension *PaiWorkspace::m_pWorkspaceExtension = NULL;
|
||
|
||
PaiWorkspace::PaiWorkspace(QWidget *pParent) :
|
||
QWidget(pParent),
|
||
m_pDropAreaHint(new PaiDropMask(this)),
|
||
m_pMainTabWidget(NULL)
|
||
{
|
||
pParent->installEventFilter(this);
|
||
setAcceptDrops(true);
|
||
m_pDropAreaHint->hide();
|
||
AcceptClosing_P(this);
|
||
}
|
||
|
||
PaiWorkspace::~PaiWorkspace()
|
||
{
|
||
m_pDropAreaHint->deleteLater();
|
||
}
|
||
|
||
void PaiWorkspace::AddWidget(const QString & areaName, QWidget *pWidget, const QString & text, const QIcon & icon)
|
||
{
|
||
if(areaName == "#")
|
||
{
|
||
if(layout() != NULL)
|
||
{
|
||
return;
|
||
}
|
||
QGridLayout *pLayout = new QGridLayout();
|
||
pLayout->setContentsMargins(0, 0, 0, 0);
|
||
pLayout->setSpacing(0);
|
||
|
||
m_pMainTabWidget = new PaiTabWidget(this);
|
||
m_pMainTabWidget->setProperty("AreaName", areaName);
|
||
connect(m_pMainTabWidget, SIGNAL(currentChanged(int)), this, SIGNAL(SubtitleChanged()));
|
||
|
||
pWidget->setParent(m_pMainTabWidget);
|
||
AddViewToTabWidget(pWidget, icon, text, m_pMainTabWidget);
|
||
connect(m_pMainTabWidget, SIGNAL(AllTabClosed(const QString&)), this, SLOT(OnAllTabRemoved()));
|
||
pLayout->addWidget(m_pMainTabWidget, 0, 0);
|
||
setLayout(pLayout);
|
||
}
|
||
}
|
||
|
||
void PaiWorkspace::dragEnterEvent(QDragEnterEvent *pEvent)
|
||
{
|
||
// QWidget::dragEnterEvent(pEvent);
|
||
// if(pEvent->mimeData()->hasFormat("text/csv"))
|
||
// {
|
||
// m_DragState.isValid = false;
|
||
// m_DragState.isDragging = false;
|
||
// QByteArray mimeData(pEvent->mimeData()->data("text/csv"));
|
||
// Unserialize(m_DragState, mimeData);
|
||
// if(m_DragState.isValid && m_DragState.isDragging && (m_DragState.appPid == QCoreApplication::applicationPid()))
|
||
// {
|
||
// pEvent->accept();
|
||
// }
|
||
// }
|
||
// else
|
||
// {
|
||
// pEvent->ignore();
|
||
// }
|
||
}
|
||
|
||
void PaiWorkspace::dragMoveEvent(QDragMoveEvent *pEvent)
|
||
{
|
||
if(m_DragState.isValid && m_DragState.isDragging)
|
||
{
|
||
QList< QSplitter* > lstSplittes = findChildren< QSplitter* > ();
|
||
for(int i = 0; i < lstSplittes.size(); ++i)
|
||
{
|
||
QSplitter *pSplitter = lstSplittes[i];
|
||
int pos = pSplitter->sizes().indexOf(0);
|
||
if(-1 != pos) // 找到大小被收缩为0的splitter索引
|
||
{
|
||
// 判断被拖动的页签将有可能被插入到splitter handle前面还是后面
|
||
bool bInsertBeforeHandle = (pos == (pSplitter->count() - 1)) ? false : true;
|
||
QSplitterHandle *pSplHandle = bInsertBeforeHandle ? pSplitter->handle(pos + 1) : pSplitter->handle(pos);
|
||
if(!pSplHandle)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// 获得splitter handle区域,并计算鼠标检测区域
|
||
QRect globalSplRect = MapToGlobal(pSplHandle, pSplHandle->rect());
|
||
if(pSplitter->orientation() == Qt::Horizontal)
|
||
{
|
||
// 若为横向splitter,以splitter handle区域为基础,向左右扩展10像素
|
||
if(bInsertBeforeHandle)
|
||
{
|
||
globalSplRect.setRight(globalSplRect.right() + 10);
|
||
}
|
||
else
|
||
{
|
||
globalSplRect.setLeft(globalSplRect.left() - 10);
|
||
}
|
||
}
|
||
else // 若为纵向splitter,以splitter handle区域为基础,向上下扩展10像素
|
||
{
|
||
if(bInsertBeforeHandle)
|
||
{
|
||
globalSplRect.setTop(globalSplRect.top() + 10);
|
||
}
|
||
else
|
||
{
|
||
globalSplRect.setBottom(globalSplRect.bottom() - 10);
|
||
}
|
||
}
|
||
// 若鼠标位置在检测区域内,则移动handle使得页签有机会被拖动到大小为0的splitter区域
|
||
if(globalSplRect.contains(QCursor::pos()))
|
||
{
|
||
QList< int > lstSize = pSplitter->sizes();
|
||
lstSize[pos] = 50;
|
||
if(bInsertBeforeHandle) // 相邻区域相应缩小
|
||
{
|
||
lstSize[pos + 1] = lstSize[pos + 1] - 50;
|
||
}
|
||
else
|
||
{
|
||
lstSize[pos - 1] = lstSize[pos - 1] - 50;
|
||
}
|
||
pSplitter->setSizes(lstSize);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// 显示虚线框
|
||
QList< QTabWidget* > lstTabWidgets = findChildren< QTabWidget* > ();
|
||
foreach(QTabWidget *pAreaWidget, lstTabWidgets)
|
||
{
|
||
QRect globalAreaRect = MapToGlobal(pAreaWidget, pAreaWidget->rect());
|
||
|
||
if(globalAreaRect.contains(QCursor::pos()) && !(m_DragState.pDraggingWidget->isAncestorOf(pAreaWidget)))
|
||
{
|
||
pEvent->accept();
|
||
m_pDropAreaHint->setGeometry(MapFromGlobal(this, globalAreaRect));
|
||
m_pDropAreaHint->show();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
pEvent->ignore();
|
||
}
|
||
|
||
void PaiWorkspace::dropEvent(QDropEvent *pEvent)
|
||
{
|
||
// if(pEvent->mimeData()->hasFormat("text/csv"))
|
||
// {
|
||
// if(pEvent->source() == NULL)
|
||
// {
|
||
// return;
|
||
// }
|
||
|
||
// // 拖拽的TabWidget,pEvent>source()应为TabBar
|
||
// PaiTabWidget *pDragTabWidget = dynamic_cast< PaiTabWidget* > (pEvent->source()->parentWidget());
|
||
|
||
// if(m_DragState.isValid && m_DragState.isDragging && pDragTabWidget)
|
||
// {
|
||
// QList< PaiTabWidget* > dropTabWidgets = findChildren< PaiTabWidget* > ();
|
||
// dropTabWidgets.removeAll(pDragTabWidget); // 排除自己
|
||
|
||
// foreach(PaiTabWidget *pDropTabWidget, dropTabWidgets)
|
||
// {
|
||
// // 如果要拖拽的TabWidget托到自己的子TabWidget上,或者该TabWidget是不接收拖拽的,则忽略掉
|
||
// if(pDragTabWidget->isAncestorOf(pDropTabWidget) || !pDropTabWidget->IsDropable())
|
||
// {
|
||
// continue;
|
||
// }
|
||
|
||
// QRect globalAreaRect = MapToGlobal(pDropTabWidget, pDropTabWidget->rect());
|
||
// if(globalAreaRect.contains(QCursor::pos()))
|
||
// {
|
||
// m_pDropAreaHint->hide();
|
||
|
||
// // 将拖拽的页签先从之前的页签区域移除
|
||
// RemoveViewFromTabWidget(m_DragState.pDraggingWidget, pDragTabWidget);
|
||
|
||
// // 由于拖拽页面功能违反了Qt的事件框架的预期,这里需要打个补丁来阻止Qt的后续事件
|
||
// QApplication::removePostedEvents(pEvent->source());
|
||
// QApplication::removePostedEvents(pDragTabWidget);
|
||
// QApplication::removePostedEvents(pDragTabWidget->currentWidget());
|
||
|
||
// // 将拖拽的页签插入到落点所在的页签区
|
||
// m_DragState.pDraggingWidget->setParent(pDropTabWidget);
|
||
// AddViewToTabWidget(m_DragState.pDraggingWidget,
|
||
// m_DragState.tabIcon,
|
||
// m_DragState.tabText,
|
||
// pDropTabWidget);
|
||
// pDropTabWidget->setCurrentWidget(m_DragState.pDraggingWidget);
|
||
// // 由于拖拽页面功能违反了Qt的事件框架的预期,这里需要打个补丁来阻止Qt的后续事件
|
||
// QApplication::removePostedEvents(this);
|
||
// QApplication::removePostedEvents(pDropTabWidget);
|
||
// QApplication::removePostedEvents(pDropTabWidget->findChild< QTabBar* > ());
|
||
// QApplication::removePostedEvents(m_DragState.pDraggingWidget);
|
||
// // 正确接受拖拽事件并返回
|
||
// pEvent->setDropAction(Qt::MoveAction);
|
||
// pEvent->accept();
|
||
// update();
|
||
// return;
|
||
// }
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// m_pDropAreaHint->hide();
|
||
// pEvent->ignore();
|
||
}
|
||
void PaiWorkspace::dragLeaveEvent(QDragLeaveEvent *pEvent)
|
||
{
|
||
m_pDropAreaHint->hide();
|
||
|
||
QList< QTabWidget* > lstTabWidgets = findChildren< QTabWidget* > ();
|
||
for(int i = 0; i < lstTabWidgets.size(); ++i)
|
||
{
|
||
QTabWidget *pTabWidget = lstTabWidgets[i];
|
||
if(pTabWidget->count() == 0)
|
||
{
|
||
// 找到空TabWidget以及其父亲splitter
|
||
QSplitter *pSplitter = qobject_cast< QSplitter* > (pTabWidget->parent());
|
||
if(pSplitter)
|
||
{
|
||
int index = pSplitter->indexOf(pTabWidget);
|
||
QList< int > lstSizes = pSplitter->sizes();
|
||
if(lstSizes.at(index) == 0)
|
||
{
|
||
continue;
|
||
}
|
||
if(index == 0) // 恢复在拖拽页签过程中显示的空TabWidget区域
|
||
{
|
||
lstSizes[index + 1] = lstSizes[index + 1] + lstSizes[index];
|
||
}
|
||
else if(index == pSplitter->count() - 1)
|
||
{
|
||
lstSizes[index - 1] = lstSizes[index - 1] + lstSizes[index];
|
||
}
|
||
lstSizes[index] = 0;
|
||
pSplitter->setSizes(lstSizes);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
QWidget::dragLeaveEvent(pEvent);
|
||
}
|
||
void PaiWorkspace::closeEvent(QCloseEvent *pEvent)
|
||
{
|
||
PaiWindow *pPaiWindow = GetFirstParentObject< PaiWindow > (this);
|
||
if(pPaiWindow && pPaiWindow->isWindow())
|
||
{
|
||
//PaiWorkspace中只支持一个顶级页签控件
|
||
PaiTabWidget *pTabWidget = findChild< PaiTabWidget* > ();
|
||
if(pTabWidget != NULL)
|
||
{
|
||
AcceptClosing_P(this);
|
||
AcceptClosing_P(pTabWidget);
|
||
pTabWidget->CloseAll();//通过发送信号关闭所有的页签,这种方式保证了如果有插件的页签关联了该信号则相关槽函数得以执行。
|
||
if(!IsCloseable(pTabWidget))
|
||
{
|
||
RejectClosing_P(this);
|
||
pEvent->ignore();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
QWidget::closeEvent(pEvent);
|
||
}
|
||
bool PaiWorkspace::eventFilter(QObject *pObject, QEvent *pEvent)
|
||
{
|
||
if(pEvent->type() == QEvent::Close)
|
||
{
|
||
close();
|
||
}
|
||
return QObject::eventFilter(pObject, pEvent);
|
||
}
|
||
|
||
void PaiWorkspace::OnAllTabRemoved()
|
||
{
|
||
PaiWindow *pPaiWindow = GetFirstParentObject< PaiWindow > (this);
|
||
if(pPaiWindow && pPaiWindow->isWindow())
|
||
{
|
||
pPaiWindow->close();
|
||
}
|
||
else if(isWindow())
|
||
{
|
||
close();
|
||
}
|
||
}
|
||
|
||
bool PaiWorkspace::AddViewToTabWidget(QWidget *pView,
|
||
const QIcon & icon,
|
||
const QString& text,
|
||
pai::gui::PaiTabWidget *pTabWidget)
|
||
{
|
||
if(m_pWorkspaceExtension)
|
||
{
|
||
return m_pWorkspaceExtension->AddViewToTabWidget(pView, icon, text, pTabWidget);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool PaiWorkspace::RemoveViewFromTabWidget(QWidget *pView, pai::gui::PaiTabWidget *pTabWidget)
|
||
{
|
||
if(m_pWorkspaceExtension)
|
||
{
|
||
return m_pWorkspaceExtension->RemoveViewFromTabWidget(pView, pTabWidget);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
PaiTabWidget* PaiWorkspace::GetPrimaryTabWidget() const
|
||
{
|
||
return m_pMainTabWidget;
|
||
}
|
||
|
||
void PaiWorkspace::SetExtension(PaiWorkspaceExtension *pExtension)
|
||
{
|
||
if(m_pWorkspaceExtension)
|
||
{
|
||
delete m_pWorkspaceExtension;
|
||
}
|
||
m_pWorkspaceExtension = pExtension;
|
||
}
|