#include "CmWindowDockManager.h" #include "CmEditorWindowManager.h" #include "CmQtEditorWindow.h" #include "CmDebug.h" #include #include #include namespace CamelotEditor { WindowDockManager::WindowDockManager(QWidget* centralWidget, QtDockOverlayWidget* dockOverlayWidget) :mDockOverlayWidget(dockOverlayWidget), mCentralWidget(centralWidget), mCentralSplitter(nullptr), mLastDraggedWindow(nullptr) { QVBoxLayout* boxLayout = new QVBoxLayout(); boxLayout->setMargin(0); boxLayout->setSpacing(0); mCentralWidget->setLayout(boxLayout); mCentralSplitter = new QSplitter(mCentralWidget); mCentralSplitter->setChildrenCollapsible(false); boxLayout->addWidget(mCentralSplitter); } void WindowDockManager::windowDragged(QtEditorWindow* window, const QPoint& mousePos) { assert(window != nullptr); if(mLastDraggedWindow != window) { mLastDragPosition = mousePos; mLastDraggedWindow = window; } if(!window->isDocked()) { Vector::type windowsToIgnore; windowsToIgnore.push_back(window->getId()); // Ignore myself QtEditorWindow* windowUnderMouse = gEditorWindowManager().getWindowAtPosition(mousePos, windowsToIgnore); QWidget* dragOverWidget = nullptr; bool contentUnderMouse = false; bool tabBarUnderMouse = false; if(windowUnderMouse != nullptr) { dragOverWidget = windowUnderMouse->getContentWidget(); QPoint localMousePos = dragOverWidget->mapFromGlobal(mousePos); if(dragOverWidget->geometry().contains(localMousePos)) contentUnderMouse = true; QWidget* tabBarWidget = windowUnderMouse->getTabWidget(); localMousePos = tabBarWidget->mapFromGlobal(mousePos); if(tabBarWidget->geometry().contains(localMousePos)) tabBarUnderMouse = true; } if(!tabBarUnderMouse && dragOverWidget == nullptr && isPositionInDockArea(mousePos)) { dragOverWidget = mCentralWidget; contentUnderMouse = true; } if(contentUnderMouse) { mDockOverlayWidget->highlightTabDropLocation(-1); mDockOverlayWidget->disableTabDropOverlay(); // Draw dock overlay if(dragOverWidget != nullptr) { WindowDragDropLocation dragLocation = getDropLocationAtPosition(dragOverWidget, mousePos); std::vector dropLocations = getDropLocations(dragOverWidget); QPoint drawOffset = mCentralWidget->mapToGlobal(QPoint(0, 0)) - mDockOverlayWidget->mapToGlobal(QPoint(0, 0)); mDockOverlayWidget->enableDropOverlay(dropLocations, drawOffset); if(dragLocation != CM_WINDROP_CENTER) mDockOverlayWidget->highlightDropLocation(dragLocation); else mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE); } else { mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE); mDockOverlayWidget->disableDropOverlay(); } } if(tabBarUnderMouse) { mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE); mDockOverlayWidget->disableDropOverlay(); // Draw tab bar overlay if(windowUnderMouse != nullptr) { std::vector tabDropLocations = windowUnderMouse->getTabBarDropLocations(); INT32 activeTabDropLocation = windowUnderMouse->getActiveTabBarDropLocation(mousePos); if(activeTabDropLocation != -1) { QPoint drawOffset = -mDockOverlayWidget->mapToGlobal(QPoint(0, 0)); mDockOverlayWidget->enableTabDropOverlay(tabDropLocations, drawOffset); mDockOverlayWidget->highlightTabDropLocation(activeTabDropLocation); } } } } else { QPoint diff = mLastDragPosition - mousePos; if(diff.manhattanLength() > 4) { undockWindow(window); } } } void WindowDockManager::windowReleased(QtEditorWindow* window, const QPoint& mousePos) { mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE); mDockOverlayWidget->disableDropOverlay(); mDockOverlayWidget->highlightTabDropLocation(-1); mDockOverlayWidget->disableTabDropOverlay(); if(mLastDraggedWindow != window) { mLastDragPosition = mousePos; mLastDraggedWindow = window; } QPoint diff = mLastDragPosition - mousePos; bool wasDragged = diff.manhattanLength() > 4; // Ensure user actually moved the window if(wasDragged && !window->isDocked()) { Vector::type windowsToIgnore; windowsToIgnore.push_back(window->getId()); // Ignore myself QtEditorWindow* windowUnderCursor = gEditorWindowManager().getWindowAtPosition(mousePos, windowsToIgnore); if(windowUnderCursor != nullptr) { bool contentUnderMouse = false; bool tabBarUnderMouse = false; QWidget* contentWidget = windowUnderCursor->getContentWidget(); QPoint localMousePos = contentWidget->mapFromGlobal(mousePos); if(contentWidget->geometry().contains(localMousePos)) contentUnderMouse = true; QWidget* tabBarWidget = windowUnderCursor->getTabWidget(); localMousePos = tabBarWidget->mapFromGlobal(mousePos); if(tabBarWidget->geometry().contains(localMousePos)) tabBarUnderMouse = true; if(contentUnderMouse) { WindowDragDropLocation dropLocation = getDropLocationAtPosition(windowUnderCursor->getContentWidget(), mousePos); dockWindow(window, windowUnderCursor, dropLocation); } else if(tabBarUnderMouse) { INT32 dropLocation = windowUnderCursor->getActiveTabBarDropLocation(mousePos); if(dropLocation != -1) { while(window->getNumWidgets() > 0) { QtEditorWidget* widget = window->getWidget(0); window->removeWidget(0); windowUnderCursor->insertWidget(dropLocation, widget); } window->closeWindow(); } } } else { if(isPositionInDockArea(mousePos)) { WindowDragDropLocation dropLocation = getDropLocationAtPosition(mCentralWidget, mousePos); dockWindow(window, nullptr, dropLocation); } } } } void WindowDockManager::windowClosed(QtEditorWindow* window) { if(window->isDocked()) undockWindow(window); } bool WindowDockManager::isDocked(const QtEditorWindow* window) const { auto findIter = mDockedWindows.find(const_cast(window)); return findIter != mDockedWindows.end(); } WindowDragDropLocation WindowDockManager::getDockLocation(const QtEditorWindow* window) const { auto findIter = mDockedWindows.find(const_cast(window)); assert(findIter != mDockedWindows.end()); return findIter->second.dockLocation; } INT32 WindowDockManager::getDockParentId(const QtEditorWindow* window) const { auto findIter = mDockedWindows.find(const_cast(window)); assert(findIter != mDockedWindows.end()); return findIter->second.parentId; } bool WindowDockManager::isPositionInDockArea(const QPoint& globalPos) { QPoint globalWidgetPos = mCentralWidget->mapToGlobal(QPoint(0, 0)); QRect widgetRect(globalWidgetPos, mCentralWidget->geometry().size()); return widgetRect.contains(globalPos); } WindowDragDropLocation WindowDockManager::getDropLocationAtPosition(const QWidget* widget, const QPoint& globalPos) { assert(widget != nullptr); QPoint localPos = mCentralWidget->mapFromGlobal(globalPos); std::vector dragLocations = getDropLocations(widget); int idx = 0; for(auto iter = dragLocations.begin(); iter != dragLocations.end(); ++iter) { if(iter->containsPoint(localPos, Qt::OddEvenFill)) return (WindowDragDropLocation)idx; ++idx; } return CM_WINDROP_NONE; } void WindowDockManager::dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition) { assert(windowToDock != nullptr); auto findIter = mDockedWindows.find(windowToDock); assert(findIter == mDockedWindows.end()); if(dockAtPosition == CM_WINDROP_NONE || dockAtPosition == CM_WINDROP_CENTER) return; if(dockAtWidget == nullptr) { mCentralSplitter->addWidget(windowToDock); windowToDock->dock(); } else { QSplitter* parentSplitter = dynamic_cast(dockAtWidget->parentWidget()); if(parentSplitter == nullptr) { LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter."); return; } if(parentSplitter->orientation() == Qt::Horizontal) { int idxDockAt = parentSplitter->indexOf(dockAtWidget); if(dockAtPosition == CM_WINDROP_LEFT) parentSplitter->insertWidget(idxDockAt, windowToDock); else if(dockAtPosition == CM_WINDROP_RIGHT) parentSplitter->insertWidget(idxDockAt + 1, windowToDock); else // Top or bottom { QSplitter* newSplitter = new QSplitter(); newSplitter->setOrientation(Qt::Vertical); newSplitter->setChildrenCollapsible(false); if(dockAtPosition == CM_WINDROP_TOP) { newSplitter->addWidget(windowToDock); newSplitter->addWidget(dockAtWidget); } else { newSplitter->addWidget(dockAtWidget); newSplitter->addWidget(windowToDock); } parentSplitter->insertWidget(idxDockAt, newSplitter); } } else { int idxDockAt = parentSplitter->indexOf(dockAtWidget); if(dockAtPosition == CM_WINDROP_TOP) parentSplitter->insertWidget(idxDockAt, windowToDock); else if(dockAtPosition == CM_WINDROP_BOTTOM) parentSplitter->insertWidget(idxDockAt + 1, windowToDock); else // Left or right { QSplitter* newSplitter = new QSplitter(); newSplitter->setOrientation(Qt::Horizontal); newSplitter->setChildrenCollapsible(false); if(dockAtPosition == CM_WINDROP_LEFT) { newSplitter->addWidget(windowToDock); newSplitter->addWidget(dockAtWidget); } else { newSplitter->addWidget(dockAtWidget); newSplitter->addWidget(windowToDock); } parentSplitter->insertWidget(idxDockAt, newSplitter); } } windowToDock->dock(); } DockedWindowInfo dockedInfo; dockedInfo.dockLocation = dockAtPosition; if(dockAtWidget == nullptr) dockedInfo.parentId = -1; else dockedInfo.parentId = dockAtWidget->getId(); mDockedWindows[windowToDock] = dockedInfo; } void WindowDockManager::undockWindow(QtEditorWindow* windowToUndock) { CM_ASSERT(windowToUndock != nullptr); QSplitter* parentSplitter = dynamic_cast(windowToUndock->parentWidget()); if(parentSplitter == nullptr) { LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter."); return; } windowToUndock->setParent(mCentralWidget); windowToUndock->undock(); windowToUndock->show(); // Check if there is just one widget in splitter, so there's no need for a splitter at all if(parentSplitter != mCentralSplitter && parentSplitter->count() == 1) { QSplitter* topParentSplitter = dynamic_cast(parentSplitter->parentWidget()); if(topParentSplitter == nullptr) { CM_EXCEPT(InternalErrorException, "Splitter is not root splitter, but doesn't have a splitter parent."); } QWidget* remainingWidget = parentSplitter->widget(0); int splitterIdx = topParentSplitter->indexOf(parentSplitter); topParentSplitter->insertWidget(splitterIdx, remainingWidget); delete parentSplitter; } auto findIter = mDockedWindows.find(windowToUndock); CM_ASSERT(findIter != mDockedWindows.end()); mDockedWindows.erase(findIter); } std::vector WindowDockManager::getDropLocations(const QWidget* widget) { QPoint topLeft = widget->mapToGlobal(QPoint(0, 0)); topLeft = mCentralWidget->mapFromGlobal(topLeft); QRect largeRect(topLeft.x() + 1, topLeft.y() + 1, widget->width() - 2, widget->height() - 2); int sizeOffset = widget->width() < widget->height() ? (widget->width() / 10) : (widget->height() / 10); QRect smallRect(topLeft.x() + sizeOffset, topLeft.y() + sizeOffset, widget->width() - 2 * sizeOffset, widget->height() - 2 * sizeOffset); QPolygon left(4); left[0] = largeRect.topLeft(); left[1] = largeRect.bottomLeft(); left[2] = smallRect.bottomLeft(); left[3] = smallRect.topLeft(); QPolygon right(4); right[0] = largeRect.topRight(); right[1] = largeRect.bottomRight(); right[2] = smallRect.bottomRight(); right[3] = smallRect.topRight(); QPolygon top(4); top[0] = largeRect.topLeft(); top[1] = largeRect.topRight(); top[2] = smallRect.topRight(); top[3] = smallRect.topLeft(); QPolygon bottom(4); bottom[0] = largeRect.bottomRight(); bottom[1] = largeRect.bottomLeft(); bottom[2] = smallRect.bottomLeft(); bottom[3] = smallRect.bottomRight(); QPolygon center(4); center[0] = smallRect.topLeft(); center[1] = smallRect.topRight(); center[2] = smallRect.bottomRight(); center[3] = smallRect.bottomLeft(); std::vector dragLocations; dragLocations.push_back(left); dragLocations.push_back(right); dragLocations.push_back(top); dragLocations.push_back(bottom); dragLocations.push_back(center); return dragLocations; } WindowDockManager& gWindowDockManager() { return WindowDockManager::instance(); } }