#include "CmQtEditorWindow.h" #include #include #include #include #include #include #include #include #include #include "CmDebug.h" #include "CmQtEditorWidget.h" #include "CmQtDynamicTabBar.h" #include "CmWindowDockManager.h" #include "CmEditorWindowManager.h" #include "CmQtEditor.h" #include "CmEditorApplication.h" namespace CamelotEditor { QtEditorWindow::QtEditorWindow(QWidget* parent, INT32 id) :QWidget(parent), mResizeMode(RM_NONE), mMoveMode(false), mIsDocked(false), mId(id), mActiveWidgetIdx(0) { setupUi(); } void QtEditorWindow::setupUi() { setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); //setAttribute(Qt::WA_NoBackground, true); //setAutoFillBackground(false); // mTimer = new QTimer(this); /************************************************************************/ /* TITLE BAR */ /************************************************************************/ mTitleBar = new QWidget(this); mTitleBar->setMouseTracking(true); mTabBar = new QtDynamicTabBar(this); mTabBar->setMouseTracking(true); mBtnClose = new QPushButton(this); mBtnUndock = new QPushButton(this); QHBoxLayout* titleLayout = new QHBoxLayout(this); titleLayout->setMargin(0); titleLayout->addWidget(mTabBar, 1); titleLayout->addWidget(mBtnUndock); titleLayout->addWidget(mBtnClose); mTitleBar->setLayout(titleLayout); /************************************************************************/ /* CENTRAL LAYOUT */ /************************************************************************/ mCentralWidget = new QWidget(this); mStackedWidget = new QStackedWidget(this); QVBoxLayout* centralLayout = new QVBoxLayout(this); centralLayout->setMargin(0); centralLayout->addWidget(mCentralWidget); setLayout(centralLayout); QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setMargin(0); mainLayout->setContentsMargins(2, 0, 2, 2); mainLayout->setSpacing(0); mainLayout->addWidget(mTitleBar); mainLayout->addWidget(mStackedWidget, 1); mCentralWidget->setLayout(mainLayout); setObjectNames(); retranslateUi(); setupSignals(); } void QtEditorWindow::setupSignals() { connect(mBtnClose, SIGNAL(clicked()), this, SLOT(closeWidget())); connect(mBtnUndock, SIGNAL(clicked()), this, SLOT(undockWidget())); connect(mTimer, SIGNAL(timeout()), this, SLOT(timerUpdate())); mTabBar->onTabSelected.connect(boost::bind(&QtEditorWindow::setActiveWidget, this, _1)); } void QtEditorWindow::retranslateUi() { mBtnUndock->setText(tr("Undock")); mBtnClose->setText(tr("Close")); } void QtEditorWindow::setObjectNames() { mTitleBar->setObjectName(QStringLiteral("TitleBar")); mTabBar->setObjectName(QStringLiteral("TabBar")); mBtnClose->setObjectName(QStringLiteral("BtnClose")); mCentralWidget->setObjectName(QStringLiteral("CentralWidget")); mStackedWidget->setObjectName(QStringLiteral("StackedWidget")); } QSizePolicy QtEditorWindow::sizePolicy() const { return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } void QtEditorWindow::undock() { if(mIsDocked) { setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); mIsDocked = false; QtEditorWidget* activeWidget = getWidget(mActiveWidgetIdx); resizeCentered(activeWidget->getDefaultSize()); } } void QtEditorWindow::dock() { if(!mIsDocked) { setWindowFlags(Qt::Widget); mIsDocked = true; } } void QtEditorWindow::resizeCentered(const QSize& size) { int newX = x() + width() / 2 - size.width() / 2; setGeometry(newX, y(), size.width(), size.height()); } WindowLayoutDesc QtEditorWindow::getLayoutDesc() const { WindowLayoutDesc desc; desc.width = geometry().width(); desc.height = geometry().height(); desc.left = geometry().x(); desc.top = geometry().y(); desc.screenIdx = -1; desc.id = getId(); desc.maximized = false; desc.dockState = WDS_FLOATING; if(isDocked()) { WindowDragDropLocation dockLocation = gWindowDockManager().getDockLocation(this); switch(dockLocation) { case CM_WINDROP_LEFT: desc.dockState = WDS_LEFT; break; case CM_WINDROP_RIGHT: desc.dockState = WDS_RIGHT; break; case CM_WINDROP_BOTTOM: desc.dockState = WDS_BOTTOM; break; case CM_WINDROP_TOP: desc.dockState = WDS_TOP; break; case CM_WINDROP_CENTER: desc.dockState = WDS_CENTER; break; default: assert(false); } desc.dockParentId = gWindowDockManager().getDockParentId(this); } else { desc.dockState = WDS_FLOATING; desc.dockParentId = -1; } desc.activeWidget = mActiveWidgetIdx; for(auto iter = mEditorWidgets.begin(); iter != mEditorWidgets.end(); ++iter) desc.childWidgetNames.push_back((*iter)->getName()); return desc; } void QtEditorWindow::restoreFromLayoutDesc(const WindowLayoutDesc& desc) { setGeometry(desc.left, desc.top, desc.width, desc.height); mId = desc.id; for(auto iter2 = desc.childWidgetNames.begin(); iter2 != desc.childWidgetNames.end(); ++iter2) gEditorWindowManager().openWidget(*iter2, this); if(desc.dockState != WDS_FLOATING) { WindowDragDropLocation dockLocation = CM_WINDROP_NONE; switch(desc.dockState) { case WDS_LEFT: dockLocation = CM_WINDROP_LEFT; break; case WDS_RIGHT: dockLocation = CM_WINDROP_RIGHT; break; case WDS_BOTTOM: dockLocation = CM_WINDROP_BOTTOM; break; case WDS_TOP: dockLocation = CM_WINDROP_TOP; break; case WDS_CENTER: dockLocation = CM_WINDROP_CENTER; break; } QtEditorWindow* dockParent = nullptr; if(desc.dockParentId != -1) dockParent = gEditorWindowManager().getOpenWindow(desc.dockParentId); gWindowDockManager().dockWindow(this, dockParent, dockLocation); } setActiveWidget(desc.activeWidget); } void QtEditorWindow::addWidget(QtEditorWidget* widget) { assert(widget != nullptr); auto iterFind = std::find(mEditorWidgets.begin(), mEditorWidgets.end(), widget); if(iterFind != mEditorWidgets.end()) CM_EXCEPT(InvalidParametersException, "Specified widget already exists in this window."); widget->setParent(this); mEditorWidgets.push_back(widget); mTabBar->addTab(widget->getName()); if(getNumWidgets() == 1) resizeCentered(widget->getDefaultSize()); } void QtEditorWindow::insertWidget(UINT32 idx, QtEditorWidget* widget) { assert(widget != nullptr); auto iterFind = std::find(mEditorWidgets.begin(), mEditorWidgets.end(), widget); if(iterFind != mEditorWidgets.end()) CM_EXCEPT(InvalidParametersException, "Specified widget already exists in this window."); if(idx > (UINT32)mEditorWidgets.size()) idx = (UINT32)mEditorWidgets.size(); widget->setParent(this); mEditorWidgets.insert(mEditorWidgets.begin() + idx, widget); mTabBar->insertTab(idx, widget->getName()); if(getNumWidgets() == 1) resizeCentered(widget->getDefaultSize()); } void QtEditorWindow::removeWidget(UINT32 idx) { if(idx >= (UINT32)mEditorWidgets.size()) CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) +". Valid range: 0 .. " + toString((UINT32)mEditorWidgets.size())); mEditorWidgets.erase(mEditorWidgets.begin() + idx); mTabBar->removeTab(idx); } QtEditorWidget* QtEditorWindow::getWidget(UINT32 idx) const { return mEditorWidgets.at(idx); } void QtEditorWindow::setActiveWidget(UINT32 idx) { if(idx >= (UINT32)mEditorWidgets.size()) CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) +". Valid range: 0 .. " + toString((UINT32)mEditorWidgets.size())); mActiveWidgetIdx = idx; // TODO } QWidget* QtEditorWindow::getContentWidget() const { return mStackedWidget; } QWidget* QtEditorWindow::getTabWidget() const { return mTabBar; } std::vector QtEditorWindow::getTabBarDropLocations() const { return mTabBar->getDropLocations(); } INT32 QtEditorWindow::getActiveTabBarDropLocation(const QPoint& mousePos) const { std::vector dropLocations = getTabBarDropLocations(); INT32 idx = 0; for(auto iter = dropLocations.begin(); iter != dropLocations.end(); ++iter) { if(iter->containsPoint(mousePos, Qt::OddEvenFill)) return idx; idx++; } return -1; } void QtEditorWindow::enterEvent(QEvent *e) { mTimer->start(100); } void QtEditorWindow::leaveEvent(QEvent *e) { mTimer->stop(); } void QtEditorWindow::mousePressEvent(QMouseEvent* event) { if(event->buttons() & Qt::LeftButton) { if(isOverResizeArea(event->globalPos())) { //setUpdatesEnabled(false); mResizeMode = getResizeMode(event->globalPos()); } else if(isOverDragArea(event->globalPos())) { mMoveMode = true; QPoint globalTopLeft = mapToGlobal(QPoint(0, 0)); mDragOffset = QPoint(globalTopLeft.x() - event->globalPos().x(), globalTopLeft.y() - event->globalPos().y()); } event->accept(); } QWidget::mousePressEvent(event); } void QtEditorWindow::mouseReleaseEvent(QMouseEvent* event) { if(mMoveMode) { gWindowDockManager().windowReleased(this, event->globalPos()); } mMoveMode = false; mResizeMode = RM_NONE; //setUpdatesEnabled(true); QWidget::mouseReleaseEvent(event); } void QtEditorWindow::mouseMoveEvent(QMouseEvent* event) { if(mMoveMode) { QPoint globalPos = mapToGlobal(event->pos()); gWindowDockManager().windowDragged(this, globalPos); if(!mIsDocked) move(event->globalPos() + mDragOffset); event->accept(); } else if(mResizeMode != RM_NONE) { if(!isDocked()) { int left = geometry().left(); int width = geometry().width(); int top = geometry().top(); int height = geometry().height(); int leftOld = left; int topOld = top; if (mResizeMode & RM_LEFT) { width = width + (left - event->globalPos().x()); left = event->globalPos().x(); } if(mResizeMode & RM_RIGHT) { width = event->globalPos().x() - left; } if(mResizeMode & RM_TOP) { height = height + (top - event->globalPos().y()); top = event->globalPos().y(); } if(mResizeMode & RM_BOTTOM) { height = event->globalPos().y() - top; } // Check if we have reached width or height limit. We need to do some special // steps otherwise the window will move (instead of resize) after the limit is reached if(width < minimumWidth()) { left = leftOld; width = minimumWidth(); } if(height < minimumHeight()) { top = topOld; height = minimumHeight(); } setGeometry(left, top, width, height); update(); } } QWidget::mouseMoveEvent(event); } void QtEditorWindow::timerUpdate() { if(!isDocked()) { if(underMouse()) { ResizeMode resizeMode = getResizeMode(QCursor::pos()); switch(resizeMode) { case RM_LEFT: setCursor(Qt::SizeHorCursor); break; case RM_RIGHT: setCursor(Qt::SizeHorCursor); break; case RM_TOP: setCursor(Qt::SizeVerCursor); break; case RM_BOTTOM: setCursor(Qt::SizeVerCursor); break; case (ResizeMode)(RM_BOTTOM | RM_RIGHT): setCursor(Qt::SizeFDiagCursor); break; case (ResizeMode)(RM_TOP | RM_LEFT): setCursor(Qt::SizeFDiagCursor); break; case (ResizeMode)(RM_BOTTOM | RM_LEFT): setCursor(Qt::SizeBDiagCursor); break; case (ResizeMode)(RM_TOP | RM_RIGHT): setCursor(Qt::SizeBDiagCursor); break; case RM_NONE: setCursor(Qt::ArrowCursor); break; } } else { if(cursor().shape() != Qt::ArrowCursor) unsetCursor(); } } } void QtEditorWindow::closeWindow() { gWindowDockManager().windowClosed(this); // TODO - Hook this up to use onClosed signal as well onClosed(this); close(); } void QtEditorWindow::closeWidget() { QtEditorWidget* activeWidget = getWidget(mActiveWidgetIdx); removeWidget(mActiveWidgetIdx); activeWidget->closeWidget(); if(getNumWidgets() == 0) closeWindow(); } void QtEditorWindow::undockWidget() { QtEditorWidget* widget = getWidget(mActiveWidgetIdx); removeWidget(mActiveWidgetIdx); QtEditorWindow* newWindow = gEditorWindowManager().openWindow(); newWindow->addWidget(widget); newWindow->resizeCentered(widget->getDefaultSize()); if(getNumWidgets() == 0) closeWindow(); } QtEditorWindow::ResizeMode QtEditorWindow::getResizeMode(QPoint mousePos) { QPoint localMousePos = mapFromGlobal(mousePos); QPoint topLeft = mapFromParent(geometry().topLeft()); QPoint botRight = mapFromParent(geometry().bottomRight()); int distFromRight = botRight.x() - localMousePos.x(); int distFromLeft = localMousePos.x() - topLeft.x(); int distFromBot = botRight.y() - localMousePos.y(); int distFromTop = localMousePos.y() - topLeft.y(); if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0) return (ResizeMode)(RM_BOTTOM | RM_RIGHT); if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0) return (ResizeMode)(RM_TOP | RM_LEFT); if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0) return (ResizeMode)(RM_BOTTOM | RM_LEFT); if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0) return (ResizeMode)(RM_TOP | RM_RIGHT); if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0) return RM_RIGHT; if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0) return RM_LEFT; if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0) return RM_BOTTOM; if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0) return RM_TOP; return RM_NONE; } bool QtEditorWindow::isOverResizeArea(QPoint mousePos) { QPoint localMousePos = mapFromGlobal(mousePos); int distFromRight = geometry().width() - localMousePos.x(); int distFromLeft = localMousePos.x(); int distFromBot = geometry().height() - localMousePos.y(); int distFromTop = localMousePos.y(); if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0) return true; if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0) return true; if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0) return true; if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0) return true; return false; } bool QtEditorWindow::isOverDragArea(QPoint mousePos) { if(getResizeMode(mousePos) != RM_NONE) return false; QPoint localMousePos = mapFromGlobal(mousePos); int distFromTop = localMousePos.y(); if(distFromTop <= TITLE_BAR_HEIGHT) return true; return false; } }