diff --git a/src/libs/advanceddockingsystem/ads_globals.cpp b/src/libs/advanceddockingsystem/ads_globals.cpp index e4615a69f87..664f10ca8a5 100644 --- a/src/libs/advanceddockingsystem/ads_globals.cpp +++ b/src/libs/advanceddockingsystem/ads_globals.cpp @@ -63,6 +63,45 @@ DockInsertParam dockAreaInsertParameters(DockWidgetArea area) return DockInsertParam(Qt::Vertical, false); } +SideBarLocation toSideBarLocation(DockWidgetArea area) +{ + switch (area) { + case LeftAutoHideArea: + return SideBarLeft; + case RightAutoHideArea: + return SideBarRight; + case TopAutoHideArea: + return SideBarTop; + case BottomAutoHideArea: + return SideBarBottom; + default: + return SideBarNone; + } + + return SideBarNone; +} + +bool isHorizontalSideBarLocation(SideBarLocation location) +{ + switch (location) { + case SideBarTop: + case SideBarBottom: + return true; + case SideBarLeft: + case SideBarRight: + return false; + default: + return false; + } + + return false; +} + +bool isSideBarArea(DockWidgetArea area) +{ + return toSideBarLocation(area) != SideBarNone; +} + QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity) { QPixmap transparentPixmap(source.size()); diff --git a/src/libs/advanceddockingsystem/ads_globals.h b/src/libs/advanceddockingsystem/ads_globals.h index ef294ba4ef1..8541e3d9da7 100644 --- a/src/libs/advanceddockingsystem/ads_globals.h +++ b/src/libs/advanceddockingsystem/ads_globals.h @@ -16,11 +16,11 @@ class QSplitter; QT_END_NAMESPACE #if defined(ADVANCEDDOCKINGSYSTEM_LIBRARY) -# define ADS_EXPORT Q_DECL_EXPORT +#define ADS_EXPORT Q_DECL_EXPORT #elif defined(ADVANCEDDOCKINGSYSTEM_STATIC_LIBRARY) -# define ADS_EXPORT +#define ADS_EXPORT #else -# define ADS_EXPORT Q_DECL_IMPORT +#define ADS_EXPORT Q_DECL_IMPORT #endif //#define ADS_DEBUG_PRINT @@ -47,14 +47,21 @@ enum DockWidgetArea { TopDockWidgetArea = 0x04, BottomDockWidgetArea = 0x08, CenterDockWidgetArea = 0x10, + LeftAutoHideArea = 0x20, + RightAutoHideArea = 0x40, + TopAutoHideArea = 0x80, + BottomAutoHideArea = 0x100, InvalidDockWidgetArea = NoDockWidgetArea, OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea | BottomDockWidgetArea, + AutoHideDockAreas = LeftAutoHideArea | RightAutoHideArea | TopAutoHideArea | BottomAutoHideArea, AllDockAreas = OuterDockAreas | CenterDockWidgetArea }; Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea) +enum eTabIndex { TabDefaultInsertIndex = -1, TabInvalidIndex = -2 }; + enum eTitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, @@ -137,6 +144,21 @@ public: */ DockInsertParam dockAreaInsertParameters(DockWidgetArea area); +/** + * Returns the SieBarLocation for the AutoHide dock widget areas. + */ +SideBarLocation toSideBarLocation(DockWidgetArea area); + +/** + * Returns true for the top or bottom side bar ansd false for the left and right side bar. + */ +bool isHorizontalSideBarLocation(SideBarLocation location); + +/** + * Returns true, if the given dock area is a SideBar area. + */ +bool isSideBarArea(DockWidgetArea area); + /** * Searches for the parent widget of the given type. Returns the parent widget of the given * widget or 0 if the widget is not child of any widget of type T. diff --git a/src/libs/advanceddockingsystem/autohidedockcontainer.cpp b/src/libs/advanceddockingsystem/autohidedockcontainer.cpp index 18a16ebaffc..cda0b14f002 100644 --- a/src/libs/advanceddockingsystem/autohidedockcontainer.cpp +++ b/src/libs/advanceddockingsystem/autohidedockcontainer.cpp @@ -90,6 +90,7 @@ struct AutoHideDockContainerPrivate ResizeHandle *m_resizeHandle = nullptr; QSize m_size; // creates invalid size QPointer m_sideTab; + QSize m_sizeCache; /** * Private data constructor @@ -181,6 +182,7 @@ AutoHideDockContainer::AutoHideDockContainer(DockWidget *dockWidget, bool opaqueResize = DockManager::testConfigFlag(DockManager::OpaqueSplitterResize); d->m_resizeHandle->setOpaqueResize(opaqueResize); d->m_size = d->m_dockArea->size(); + d->m_sizeCache = dockWidget->size(); addDockWidget(dockWidget); parent->registerAutoHideWidget(this); @@ -228,6 +230,11 @@ void AutoHideDockContainer::updateSize() default: break; } + + if (orientation() == Qt::Horizontal) + d->m_sizeCache.setHeight(this->height()); + else + d->m_sizeCache.setWidth(this->width()); } AutoHideDockContainer::~AutoHideDockContainer() @@ -245,13 +252,13 @@ AutoHideDockContainer::~AutoHideDockContainer() delete d; } -AutoHideSideBar *AutoHideDockContainer::sideBar() const +AutoHideSideBar *AutoHideDockContainer::autoHideSideBar() const { if (d->m_sideTab) { return d->m_sideTab->sideBar(); } else { auto container = dockContainer(); - return container ? container->sideTabBar(d->m_sideTabBarArea) : nullptr; + return container ? container->autoHideSideBar(d->m_sideTabBarArea) : nullptr; } } @@ -277,14 +284,18 @@ void AutoHideDockContainer::addDockWidget(DockWidget *dockWidget) DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget(); auto isRestoringState = dockWidget->dockManager()->isRestoringState(); if (oldDockArea && !isRestoringState) { - // The initial size should be a little bit bigger than the original dock - // area size to prevent that the resize handle of this auto hid dock area - // is near of the splitter of the old dock area. + // The initial size should be a little bit bigger than the original dock area size to + // prevent that the resize handle of this auto hid dock area is near of the splitter of + // the old dock area. d->m_size = oldDockArea->size() + QSize(16, 16); oldDockArea->removeDockWidget(dockWidget); } d->m_dockArea->addDockWidget(dockWidget); updateSize(); + // The dock area is not visible and will not update the size when updateSize() is called for + // this auto hide container. Therefore we explicitly resize it here. As soon as it will + // become visible, it will get the right size + d->m_dockArea->resize(size()); } SideBarLocation AutoHideDockContainer::sideBarLocation() const @@ -393,6 +404,39 @@ void AutoHideDockContainer::setSize(int size) updateSize(); } +Qt::Orientation AutoHideDockContainer::orientation() const +{ + return internal::isHorizontalSideBarLocation(d->m_sideTabBarArea) ? Qt::Horizontal + : Qt::Vertical; +} + +void AutoHideDockContainer::resetToInitialDockWidgetSize() +{ + if (orientation() == Qt::Horizontal) + setSize(d->m_sizeCache.height()); + else + setSize(d->m_sizeCache.width()); +} + +void AutoHideDockContainer::moveToNewSideBarLocation(SideBarLocation newSideBarLocation, int index) +{ + if (newSideBarLocation == sideBarLocation() && index == tabIndex()) + return; + + auto oldOrientation = orientation(); + auto sideBar = dockContainer()->autoHideSideBar(newSideBarLocation); + sideBar->addAutoHideWidget(this, index); + // If we move a horizontal auto hide container to a vertical position then we resize it to the + // orginal dock widget size, to avoid an extremely stretched dock widget after insertion. + if (sideBar->orientation() != oldOrientation) + resetToInitialDockWidgetSize(); +} + +int AutoHideDockContainer::tabIndex() const +{ + return d->m_sideTab->tabIndex(); +} + /** * Returns true if the object given in ancestor is an ancestor of the object given in descendant */ diff --git a/src/libs/advanceddockingsystem/autohidedockcontainer.h b/src/libs/advanceddockingsystem/autohidedockcontainer.h index ea844a330a4..ac60c86c7ea 100644 --- a/src/libs/advanceddockingsystem/autohidedockcontainer.h +++ b/src/libs/advanceddockingsystem/autohidedockcontainer.h @@ -66,7 +66,7 @@ public: /** * Get's the side tab bar */ - AutoHideSideBar *sideBar() const; + AutoHideSideBar *autoHideSideBar() const; /** * Returns the side tab @@ -137,6 +137,32 @@ public: * Depending on the sidebar location this will set the width or height of this auto hide container. */ void setSize(int size); + + /** + * Returns orientation of this container. + * Left and right containers have a Qt::Vertical orientation and top / bottom containers have + * a Qt::Horizontal orientation. The function returns the orientation of the corresponding + * auto hide side bar. + */ + Qt::Orientation orientation() const; + + /** + * Resets the with or hight to the initial dock widget size dependinng on the orientation. + * If the orientation is Qt::Horizontal, then the height is reset to the initial size and if + * orientation is Qt::Vertical, then the width is reset to the initial size. + */ + void resetToInitialDockWidgetSize(); + + /** + * Removes the AutoHide container from the current side bar and adds it to the new side bar + * given in SideBarLocation. + */ + void moveToNewSideBarLocation(SideBarLocation sideBarLocation, int index = -1); + + /** + * Returns the index of this container in the sidebar. + */ + int tabIndex() const; }; } // namespace ADS diff --git a/src/libs/advanceddockingsystem/autohidesidebar.cpp b/src/libs/advanceddockingsystem/autohidesidebar.cpp index e5c3dbcb090..4005f837b5f 100644 --- a/src/libs/advanceddockingsystem/autohidesidebar.cpp +++ b/src/libs/advanceddockingsystem/autohidesidebar.cpp @@ -147,6 +147,7 @@ void AutoHideSideBar::insertTab(int index, AutoHideTab *sideTab) { sideTab->setSideBar(this); sideTab->installEventFilter(this); + // Default insertion is append if (index < 0) d->m_tabsLayout->insertWidget(d->m_tabsLayout->count() - 1, sideTab); else @@ -177,11 +178,20 @@ void AutoHideSideBar::removeAutoHideWidget(AutoHideDockContainer *autoHideWidget autoHideWidget->setParent(nullptr); } -void AutoHideSideBar::addAutoHideWidget(AutoHideDockContainer *autoHideWidget) +void AutoHideSideBar::addAutoHideWidget(AutoHideDockContainer *autoHideWidget, int index) { auto sideBar = autoHideWidget->autoHideTab()->sideBar(); - if (sideBar == this) - return; + if (sideBar == this) { + // If we move to the same tab index or if we insert before the next tab index, then we will + // end at the same tab position and can leave. + if (autoHideWidget->tabIndex() == index || (autoHideWidget->tabIndex() + 1) == index) + return; + + // We remove this auto hide widget from the sidebar in the code below and therefore need + // to correct the TabIndex here. + if (autoHideWidget->tabIndex() < index) + --index; + } if (sideBar) sideBar->removeAutoHideWidget(autoHideWidget); @@ -189,7 +199,7 @@ void AutoHideSideBar::addAutoHideWidget(AutoHideDockContainer *autoHideWidget) autoHideWidget->setParent(d->m_containerWidget); autoHideWidget->setSideBarLocation(d->m_sideTabArea); d->m_containerWidget->registerAutoHideWidget(autoHideWidget); - insertTab(-1, autoHideWidget->autoHideTab()); + insertTab(index, autoHideWidget->autoHideTab()); } void AutoHideSideBar::removeTab(AutoHideTab *sideTab) @@ -228,33 +238,73 @@ Qt::Orientation AutoHideSideBar::orientation() const return d->m_orientation; } -AutoHideTab *AutoHideSideBar::tabAt(int index) const +AutoHideTab *AutoHideSideBar::tab(int index) const { return qobject_cast(d->m_tabsLayout->itemAt(index)->widget()); } -int AutoHideSideBar::tabCount() const +int AutoHideSideBar::tabAt(const QPoint &pos) const +{ + if (!isVisible()) + return TabInvalidIndex; + + if (orientation() == Qt::Horizontal) { + if (pos.x() < tab(0)->geometry().x()) + return -1; + } else { + if (pos.y() < tab(0)->geometry().y()) + return -1; + } + + for (int i = 0; i < count(); ++i) { + if (tab(i)->geometry().contains(pos)) + return i; + } + + return count(); +} + +int AutoHideSideBar::tabInsertIndexAt(const QPoint &pos) const +{ + int index = tabAt(pos); + if (index == TabInvalidIndex) + return TabDefaultInsertIndex; + else + return (index < 0) ? 0 : index; +} + +int AutoHideSideBar::indexOfTab(const AutoHideTab &autoHideTab) const +{ + for (auto i = 0; i < count(); i++) { + if (tab(i) == &autoHideTab) + return i; + } + + return -1; +} + +int AutoHideSideBar::count() const { return d->m_tabsLayout->count() - 1; } int AutoHideSideBar::visibleTabCount() const { - int count = 0; + int c = 0; auto parent = parentWidget(); - for (auto i = 0; i < tabCount(); i++) { - if (tabAt(i)->isVisibleTo(parent)) - count++; + for (auto i = 0; i < count(); i++) { + if (tab(i)->isVisibleTo(parent)) + c++; } - return count; + return c; } bool AutoHideSideBar::hasVisibleTabs() const { auto parent = parentWidget(); - for (auto i = 0; i < tabCount(); i++) { - if (tabAt(i)->isVisibleTo(parent)) + for (auto i = 0; i < count(); i++) { + if (tab(i)->isVisibleTo(parent)) return true; } @@ -268,19 +318,19 @@ SideBarLocation AutoHideSideBar::sideBarLocation() const void AutoHideSideBar::saveState(QXmlStreamWriter &s) const { - if (!tabCount()) + if (!count()) return; s.writeStartElement("sideBar"); s.writeAttribute("area", QString::number(sideBarLocation())); - s.writeAttribute("tabs", QString::number(tabCount())); + s.writeAttribute("tabs", QString::number(count())); - for (auto i = 0; i < tabCount(); ++i) { - auto tab = tabAt(i); - if (!tab) + for (auto i = 0; i < count(); ++i) { + auto currentTab = tab(i); + if (!currentTab) continue; - tab->dockWidget()->autoHideDockContainer()->saveState(s); + currentTab->dockWidget()->autoHideDockContainer()->saveState(s); } s.writeEndElement(); diff --git a/src/libs/advanceddockingsystem/autohidesidebar.h b/src/libs/advanceddockingsystem/autohidesidebar.h index 0376fd4ba35..7477ca8c499 100644 --- a/src/libs/advanceddockingsystem/autohidesidebar.h +++ b/src/libs/advanceddockingsystem/autohidesidebar.h @@ -91,7 +91,7 @@ public: * Adds the given AutoHideWidget to this sidebar. If the AutoHideWidget is in another sidebar, * then it will be removed from this sidebar. */ - void addAutoHideWidget(AutoHideDockContainer *autoHideWidget); + void addAutoHideWidget(AutoHideDockContainer *autoHideWidget, int index = TabDefaultInsertIndex); /** * Returns orientation of side tab. @@ -101,12 +101,29 @@ public: /** * Get the side tab widget at position, returns nullptr if it's out of bounds. */ - AutoHideTab *tabAt(int index) const; + AutoHideTab *tab(int index) const; + + /** + * Returns the tab at the given position. + * Returns -1 if the position is left of the first tab and count() if the position is right + * of the last tab. Returns InvalidTabIndex (-2) to indicate an invalid value. + */ + int tabAt(const QPoint &pos) const; + + /** + * Returns the tab insertion index for the given mouse cursor position. + */ + int tabInsertIndexAt(const QPoint &pos) const; + + /** + * Returns the index of the given tab. + */ + int indexOfTab(const AutoHideTab &tab) const; /** * Gets the count of the tab widgets. */ - int tabCount() const; + int count() const; /** * Returns the number of visible tabs to its parent widget. diff --git a/src/libs/advanceddockingsystem/autohidetab.cpp b/src/libs/advanceddockingsystem/autohidetab.cpp index 1036856b402..c6bfd7bdb8b 100644 --- a/src/libs/advanceddockingsystem/autohidetab.cpp +++ b/src/libs/advanceddockingsystem/autohidetab.cpp @@ -6,14 +6,22 @@ #include "ads_globals_p.h" #include "autohidedockcontainer.h" #include "autohidesidebar.h" +#include "dockareawidget.h" #include "dockmanager.h" +#include "dockoverlay.h" #include "dockwidget.h" +#include "floatingdragpreview.h" #include #include +#include #include +#include namespace ADS { + +static const char *const g_locationProperty = "Location"; + /** * Private data class of CDockWidgetTab class (pimpl) */ @@ -24,6 +32,12 @@ struct AutoHideTabPrivate AutoHideSideBar *m_sideBar = nullptr; Qt::Orientation m_orientation{Qt::Vertical}; QElapsedTimer m_timeSinceHoverMousePress; + bool m_mousePressed = false; + eDragState m_dragState = DraggingInactive; + QPoint m_globalDragStartMousePosition; + QPoint m_dragStartMousePosition; + AbstractFloatingWidget *m_floatingWidget = nullptr; + Qt::Orientation m_dragStartOrientation; /** * Private data constructor @@ -52,6 +66,48 @@ struct AutoHideTabPrivate if (container) container->handleAutoHideWidgetEvent(event, q); } + + /** + * Helper function to create and initialize the menu entries for the "Auto Hide Group To..." menu. + */ + QAction *createAutoHideToAction(const QString &title, SideBarLocation location, QMenu *menu) + { + auto action = menu->addAction(title); + action->setProperty("Location", location); + QObject::connect(action, &QAction::triggered, q, &AutoHideTab::onAutoHideToActionClicked); + action->setEnabled(location != q->sideBarLocation()); + return action; + } + + /** + * Test function for current drag state. + */ + bool isDraggingState(eDragState dragState) const { return m_dragState == dragState; } + + /** + * Saves the drag start position in global and local coordinates. + */ + void saveDragStartMousePosition(const QPoint &globalPos) + { + m_globalDragStartMousePosition = globalPos; + m_dragStartMousePosition = q->mapFromGlobal(globalPos); + } + + /** + * Starts floating of the dock widget that belongs to this title bar + * Returns true, if floating has been started and false if floating is not possible for any reason. + */ + bool startFloating(eDragState draggingState = DraggingFloatingWidget); + + template + AbstractFloatingWidget *createFloatingWidget(T *widget) + { + auto w = new FloatingDragPreview(widget); + q->connect(w, &FloatingDragPreview::draggingCanceled, [=]() { + m_dragState = DraggingInactive; + }); + return w; + } }; // struct DockWidgetTabPrivate AutoHideTabPrivate::AutoHideTabPrivate(AutoHideTab *parent) @@ -71,25 +127,47 @@ void AutoHideTabPrivate::updateOrientation() } } -void AutoHideTab::setSideBar(AutoHideSideBar *sideTabBar) +bool AutoHideTabPrivate::startFloating(eDragState draggingState) { - d->m_sideBar = sideTabBar; - if (d->m_sideBar) - d->updateOrientation(); -} + auto dockArea = m_dockWidget->dockAreaWidget(); + qCInfo(adsLog) << Q_FUNC_INFO << "isFloating " << dockContainer()->isFloating(); -AutoHideSideBar *AutoHideTab::sideBar() const -{ - return d->m_sideBar; -} + m_dragState = draggingState; + AbstractFloatingWidget *floatingWidget = nullptr; + floatingWidget = createFloatingWidget(dockArea); + auto size = dockArea->size(); + auto startPos = m_dragStartMousePosition; + auto autoHideContainer = m_dockWidget->autoHideDockContainer(); + m_dragStartOrientation = autoHideContainer->orientation(); -void AutoHideTab::removeFromSideBar() -{ - if (d->m_sideBar == nullptr) - return; + switch (m_sideBar->sideBarLocation()) { + case SideBarLeft: + startPos.rx() = autoHideContainer->rect().left() + 10; + break; - d->m_sideBar->removeTab(this); - setSideBar(nullptr); + case SideBarRight: + startPos.rx() = autoHideContainer->rect().right() - 10; + break; + + case SideBarTop: + startPos.ry() = autoHideContainer->rect().top() + 10; + break; + + case SideBarBottom: + startPos.ry() = autoHideContainer->rect().bottom() - 10; + break; + + case SideBarNone: + return false; + } + floatingWidget->startFloating(startPos, size, DraggingFloatingWidget, q); + auto dockManager = m_dockWidget->dockManager(); + auto overlay = dockManager->containerOverlay(); + overlay->setAllowedAreas(OuterDockAreas); + m_floatingWidget = floatingWidget; + qApp->postEvent(m_dockWidget, new QEvent((QEvent::Type) internal::g_dockedWidgetDragStartEvent)); + + return true; } AutoHideTab::AutoHideTab(QWidget *parent) @@ -164,6 +242,61 @@ void AutoHideTab::setDockWidget(DockWidget *dockWidget) setIcon(d->m_dockWidget->icon()); setToolTip(dockWidget->windowTitle()); } +bool AutoHideTab::iconOnly() const +{ + return DockManager::testAutoHideConfigFlag(DockManager::AutoHideSideBarsIconOnly) + && !icon().isNull(); +} + +AutoHideSideBar *AutoHideTab::sideBar() const +{ + return d->m_sideBar; +} + +int AutoHideTab::tabIndex() const +{ + if (!d->m_sideBar) + return -1; + + return d->m_sideBar->indexOfTab(*this); +} + +void AutoHideTab::setDockWidgetFloating() +{ + d->m_dockWidget->setFloating(); +} + +void AutoHideTab::unpinDockWidget() +{ + d->m_dockWidget->setAutoHide(false); +} + +void AutoHideTab::requestCloseDockWidget() +{ + d->m_dockWidget->requestCloseDockWidget(); +} + +void AutoHideTab::onAutoHideToActionClicked() +{ + int location = sender()->property(g_locationProperty).toInt(); + d->m_dockWidget->setAutoHide(true, (SideBarLocation) location); +} + +void AutoHideTab::setSideBar(AutoHideSideBar *sideTabBar) +{ + d->m_sideBar = sideTabBar; + if (d->m_sideBar) + d->updateOrientation(); +} + +void AutoHideTab::removeFromSideBar() +{ + if (d->m_sideBar == nullptr) + return; + + d->m_sideBar->removeTab(this); + setSideBar(nullptr); +} bool AutoHideTab::event(QEvent *event) { @@ -194,10 +327,138 @@ bool AutoHideTab::event(QEvent *event) return Super::event(event); } -bool AutoHideTab::iconOnly() const +void AutoHideTab::contextMenuEvent(QContextMenuEvent *event) { - return DockManager::testAutoHideConfigFlag(DockManager::AutoHideSideBarsIconOnly) - && !icon().isNull(); + event->accept(); + d->saveDragStartMousePosition(event->globalPos()); + + const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable); + QMenu menu(this); + + QAction *detachAction = menu.addAction(tr("Detach")); + detachAction->connect(detachAction, + &QAction::triggered, + this, + &AutoHideTab::setDockWidgetFloating); + detachAction->setEnabled(isFloatable); + + auto isPinnable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetPinnable); + detachAction->setEnabled(isPinnable); + + auto pinToMenu = menu.addMenu(tr("Pin To...")); + pinToMenu->setEnabled(isPinnable); + d->createAutoHideToAction(tr("Top"), SideBarTop, pinToMenu); + d->createAutoHideToAction(tr("Left"), SideBarLeft, pinToMenu); + d->createAutoHideToAction(tr("Right"), SideBarRight, pinToMenu); + d->createAutoHideToAction(tr("Bottom"), SideBarBottom, pinToMenu); + + QAction *unpinAction = menu.addAction(tr("Unpin (Dock)")); + unpinAction->connect(unpinAction, &QAction::triggered, this, &AutoHideTab::unpinDockWidget); + menu.addSeparator(); + QAction *closeAction = menu.addAction(tr("Close")); + closeAction->connect(closeAction, + &QAction::triggered, + this, + &AutoHideTab::requestCloseDockWidget); + closeAction->setEnabled(d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable)); + + menu.exec(event->globalPos()); +} + +void AutoHideTab::mousePressEvent(QMouseEvent *event) +{ + // If AutoHideShowOnMouseOver is active, then the showing is triggered by a MousePressEvent + // sent to this tab. To prevent accidental hiding of the tab by a mouse click, we wait at + // least 500 ms before we accept the mouse click. + if (!event->spontaneous()) { + d->m_timeSinceHoverMousePress.restart(); + d->forwardEventToDockContainer(event); + } else if (d->m_timeSinceHoverMousePress.hasExpired(500)) { + d->forwardEventToDockContainer(event); + } + + if (event->button() == Qt::LeftButton) { + event->accept(); + d->m_mousePressed = true; + d->saveDragStartMousePosition(event->globalPosition().toPoint()); + d->m_dragState = DraggingMousePressed; + } + + Super::mousePressEvent(event); +} + +void AutoHideTab::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + d->m_mousePressed = false; + auto currentDragState = d->m_dragState; + d->m_globalDragStartMousePosition = QPoint(); + d->m_dragStartMousePosition = QPoint(); + d->m_dragState = DraggingInactive; + + switch (currentDragState) { + case DraggingTab: + // End of tab moving, emit signal + //if (d->DockArea) { + // event->accept(); + // Q_EMIT moved(internal::globalPositionOf(event)); + //} + break; + + case DraggingFloatingWidget: + event->accept(); + d->m_floatingWidget->finishDragging(); + if (d->m_dockWidget->isAutoHide() && d->m_dragStartOrientation != orientation()) + d->m_dockWidget->autoHideDockContainer()->resetToInitialDockWidgetSize(); + + break; + + default: + break; // do nothing + } + } + + Super::mouseReleaseEvent(event); +} + +void AutoHideTab::mouseMoveEvent(QMouseEvent *event) +{ + if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) { + d->m_dragState = DraggingInactive; + Super::mouseMoveEvent(event); + return; + } + + // Move floating window + if (d->isDraggingState(DraggingFloatingWidget)) { + d->m_floatingWidget->moveFloating(); + Super::mouseMoveEvent(event); + return; + } + + // move tab + if (d->isDraggingState(DraggingTab)) { + // Moving the tab is always allowed because it does not mean moving the dock widget around. + //d->moveTab(ev); + } + + auto mappedPos = mapToParent(event->pos()); + bool mouseOutsideBar = (mappedPos.x() < 0) || (mappedPos.x() > parentWidget()->rect().right()); + // Maybe a fixed drag distance is better here ? + int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y() + - event->globalPosition().toPoint().y()); + if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) { + // Floating is only allowed for widgets that are floatable + // We can create the drag preview if the widget is movable. + auto Features = d->m_dockWidget->features(); + if (Features.testFlag(DockWidget::DockWidgetFloatable) + || (Features.testFlag(DockWidget::DockWidgetMovable))) { + d->startFloating(); + } + return; + } + + Super::mouseMoveEvent(event); } } // namespace ADS diff --git a/src/libs/advanceddockingsystem/autohidetab.h b/src/libs/advanceddockingsystem/autohidetab.h index ae74542914a..d8a4a2a1e61 100644 --- a/src/libs/advanceddockingsystem/autohidetab.h +++ b/src/libs/advanceddockingsystem/autohidetab.h @@ -38,10 +38,16 @@ private: friend class DockContainerWidget; friend DockContainerWidgetPrivate; + void onAutoHideToActionClicked(); + protected: void setSideBar(AutoHideSideBar *sideTabBar); void removeFromSideBar(); virtual bool event(QEvent *event) override; + virtual void contextMenuEvent(QContextMenuEvent *event) override; + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; public: using Super = PushButton; @@ -102,6 +108,26 @@ public: * Returns the side bar that contains this tab or a nullptr if the tab is not in a side bar. */ AutoHideSideBar *sideBar() const; + + /** + * Returns the index of this tab in the sideBar. + */ + int tabIndex() const; + + /** + * Set the dock widget floating, if it is floatable + */ + void setDockWidgetFloating(); + + /** + * Unpin and dock the auto hide widget. + */ + void unpinDockWidget(); + + /** + * Calls the requestCloseDockWidget() function for the assigned dock widget. + */ + void requestCloseDockWidget(); }; // class AutoHideTab } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp index 7aa9a687c06..e1ac0c9b861 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.cpp +++ b/src/libs/advanceddockingsystem/dockareatabbar.cpp @@ -104,6 +104,75 @@ DockAreaTabBar::~DockAreaTabBar() delete d; } +void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab) +{ + const int index = d->m_tabsLayout->indexOf(sourceTab); + if (index < 0) + return; + + setCurrentIndex(index); + emit tabBarClicked(index); +} + +void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab) +{ + const int index = d->m_tabsLayout->indexOf(sourceTab); + closeTab(index); +} + +void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab) +{ + for (int i = 0; i < count(); ++i) { + auto currentTab = tab(i); + if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) { + // If the dock widget is deleted with the closeTab() call, its tab it will no longer + // be in the layout, and thus the index needs to be updated to not skip any tabs. + int offset = currentTab->dockWidget()->features().testFlag( + DockWidget::DockWidgetDeleteOnClose) + ? 1 + : 0; + closeTab(i); + // If the the dock widget blocks closing, i.e. if the flag CustomCloseHandling is set, + // and the dock widget is still open, then we do not need to correct the index. + if (currentTab->dockWidget()->isClosed()) + i -= offset; + } + } +} + +void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition) +{ + const int fromIndex = d->m_tabsLayout->indexOf(sourceTab); + auto mousePos = mapFromGlobal(globalPosition); + mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x()); + mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x()); + int toIndex = -1; + // Find tab under mouse + for (int i = 0; i < count(); ++i) { + DockWidgetTab *dropTab = tab(i); + if (dropTab == sourceTab || !dropTab->isVisibleTo(this) + || !dropTab->geometry().contains(mousePos)) + continue; + + toIndex = d->m_tabsLayout->indexOf(dropTab); + if (toIndex == fromIndex) + toIndex = -1; + + break; + } + + if (toIndex > -1) { + d->m_tabsLayout->removeWidget(sourceTab); + d->m_tabsLayout->insertWidget(toIndex, sourceTab); + qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex; + emit tabMoved(fromIndex, toIndex); + setCurrentIndex(toIndex); + } else { + // Ensure that the moved tab is reset to its start position + d->m_tabsLayout->update(); + } +} + void DockAreaTabBar::wheelEvent(QWheelEvent *event) { event->accept(); @@ -114,23 +183,6 @@ void DockAreaTabBar::wheelEvent(QWheelEvent *event) horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20); } -void DockAreaTabBar::setCurrentIndex(int index) -{ - if (index == d->m_currentIndex) - return; - - if (index < -1 || index > (count() - 1)) { - qWarning() << Q_FUNC_INFO << "Invalid index" << index; - return; - } - - emit currentChanging(index); - d->m_currentIndex = index; - d->updateTabs(); - updateGeometry(); - emit currentChanged(index); -} - int DockAreaTabBar::count() const { // The tab bar contains a stretch item as last item @@ -226,42 +278,6 @@ DockWidgetTab *DockAreaTabBar::currentTab() const return qobject_cast(d->m_tabsLayout->itemAt(d->m_currentIndex)->widget()); } -void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab) -{ - const int index = d->m_tabsLayout->indexOf(sourceTab); - if (index < 0) - return; - - setCurrentIndex(index); - emit tabBarClicked(index); -} - -void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab) -{ - const int index = d->m_tabsLayout->indexOf(sourceTab); - closeTab(index); -} - -void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab) -{ - for (int i = 0; i < count(); ++i) { - auto currentTab = tab(i); - if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) { - // If the dock widget is deleted with the closeTab() call, its tab it will no longer - // be in the layout, and thus the index needs to be updated to not skip any tabs. - int offset = currentTab->dockWidget()->features().testFlag( - DockWidget::DockWidgetDeleteOnClose) - ? 1 - : 0; - closeTab(i); - // If the the dock widget blocks closing, i.e. if the flag CustomCloseHandling is set, - // and the dock widget is still open, then we do not need to correct the index. - if (currentTab->dockWidget()->isClosed()) - i -= offset; - } - } -} - DockWidgetTab *DockAreaTabBar::tab(int index) const { if (index >= count() || index < 0) @@ -270,49 +286,29 @@ DockWidgetTab *DockAreaTabBar::tab(int index) const return qobject_cast(d->m_tabsLayout->itemAt(index)->widget()); } -void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition) +int DockAreaTabBar::tabAt(const QPoint &pos) const { - const int fromIndex = d->m_tabsLayout->indexOf(sourceTab); - auto mousePos = mapFromGlobal(globalPosition); - mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x()); - mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x()); - int toIndex = -1; - // Find tab under mouse + if (!isVisible()) + return TabInvalidIndex; + + if (pos.x() < tab(0)->geometry().x()) + return -1; + for (int i = 0; i < count(); ++i) { - DockWidgetTab *dropTab = tab(i); - if (dropTab == sourceTab || !dropTab->isVisibleTo(this) - || !dropTab->geometry().contains(mousePos)) - continue; - - toIndex = d->m_tabsLayout->indexOf(dropTab); - if (toIndex == fromIndex) - toIndex = -1; - - break; + if (tab(i)->geometry().contains(pos)) + return i; } - if (toIndex > -1) { - d->m_tabsLayout->removeWidget(sourceTab); - d->m_tabsLayout->insertWidget(toIndex, sourceTab); - qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex; - emit tabMoved(fromIndex, toIndex); - setCurrentIndex(toIndex); - } else { - // Ensure that the moved tab is reset to its start position - d->m_tabsLayout->update(); - } + return count(); } -void DockAreaTabBar::closeTab(int index) +int DockAreaTabBar::tabInsertIndexAt(const QPoint &pos) const { - if (index < 0 || index >= count()) - return; - - auto dockWidgetTab = tab(index); - if (dockWidgetTab->isHidden()) - return; - - emit tabCloseRequested(index); + int index = tabAt(pos); + if (index == TabInvalidIndex) + return TabDefaultInsertIndex; + else + return (index < 0) ? 0 : index; } bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event) @@ -362,4 +358,33 @@ QSize DockAreaTabBar::sizeHint() const return d->m_tabsContainerWidget->sizeHint(); } +void DockAreaTabBar::setCurrentIndex(int index) +{ + if (index == d->m_currentIndex) + return; + + if (index < -1 || index > (count() - 1)) { + qWarning() << Q_FUNC_INFO << "Invalid index" << index; + return; + } + + emit currentChanging(index); + d->m_currentIndex = index; + d->updateTabs(); + updateGeometry(); + emit currentChanged(index); +} + +void DockAreaTabBar::closeTab(int index) +{ + if (index < 0 || index >= count()) + return; + + auto dockWidgetTab = tab(index); + if (dockWidgetTab->isHidden()) + return; + + emit tabCloseRequested(index); +} + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockareatabbar.h b/src/libs/advanceddockingsystem/dockareatabbar.h index 7cde0192634..c21b577947f 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.h +++ b/src/libs/advanceddockingsystem/dockareatabbar.h @@ -86,6 +86,18 @@ public: */ DockWidgetTab *tab(int index) const; + /** + * Returns the tab at the given position. + * Returns -1 if the position is left of the first tab and count() if the position is right + * of the last tab. Returns -2 to indicate an invalid value. + */ + int tabAt(const QPoint &pos) const; + + /** + * Returns the tab insertion index for the given mouse cursor position. + */ + int tabInsertIndexAt(const QPoint &pos) const; + /** * Filters the tab widget events */ diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 273984855c1..234916d530d 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -44,10 +44,10 @@ class DockAreaTitleBarPrivate { public: DockAreaTitleBar *q; - QPointer m_tabsMenuButton; - QPointer m_autoHideButton; - QPointer m_undockButton; - QPointer m_closeButton; + QPointer m_tabsMenuButton; + QPointer m_autoHideButton; + QPointer m_undockButton; + QPointer m_closeButton; QBoxLayout *m_layout = nullptr; DockAreaWidget *m_dockArea = nullptr; DockAreaTabBar *m_tabBar = nullptr; @@ -306,9 +306,9 @@ void DockAreaTitleBarPrivate::startFloating(const QPoint &offset) qApp->postEvent(m_dockArea, new QEvent((QEvent::Type) internal::g_dockedWidgetDragStartEvent)); } -TitleBarButton::TitleBarButton(bool visible, QWidget *parent) +TitleBarButton::TitleBarButton(bool showInTitleBar, QWidget *parent) : TitleBarButtonType(parent) - , m_visible(visible) + , m_showInTitleBar(showInTitleBar) , m_hideWhenDisabled( DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons)) { @@ -317,8 +317,8 @@ TitleBarButton::TitleBarButton(bool visible, QWidget *parent) void TitleBarButton::setVisible(bool visible) { - // 'visible' can stay 'true' if and only if this button is configured to generaly visible: - visible = visible && m_visible; + // 'visible' can stay 'true' if and only if this button is configured to generally visible: + visible = visible && m_showInTitleBar; // 'visible' can stay 'true' unless: this button is configured to be invisible when it // is disabled and it is currently disabled: @@ -328,6 +328,14 @@ void TitleBarButton::setVisible(bool visible) Super::setVisible(visible); } +void TitleBarButton::setShowInTitleBar(bool show) +{ + m_showInTitleBar = show; + + if (!show) + setVisible(false); +} + bool TitleBarButton::event(QEvent *event) { if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) { @@ -524,7 +532,7 @@ void DockAreaTitleBar::onAutoHideToActionClicked() d->m_dockArea->toggleAutoHide((SideBarLocation) location); } -QAbstractButton *DockAreaTitleBar::button(eTitleBarButton which) const +TitleBarButton *DockAreaTitleBar::button(eTitleBarButton which) const { switch (which) { case TitleBarButtonTabsMenu: @@ -733,4 +741,19 @@ QString DockAreaTitleBar::titleBarButtonToolTip(eTitleBarButton button) const return QString(); } +void DockAreaTitleBar::setAreaFloating() +{ + // If this is the last dock area in a dock container it does not make sense to move it to + // a new floating widget and leave this one empty. + auto dockContainer = d->m_dockArea->dockContainer(); + if (dockContainer->isFloating() && dockContainer->dockAreaCount() == 1 + && !d->m_dockArea->isAutoHide()) + return; + + if (!d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) + return; + + d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); +} + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.h b/src/libs/advanceddockingsystem/dockareatitlebar.h index c1c4603fd73..738dea476de 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.h +++ b/src/libs/advanceddockingsystem/dockareatitlebar.h @@ -30,17 +30,22 @@ using TitleBarButtonType = QToolButton; class TitleBarButton : public TitleBarButtonType { Q_OBJECT - bool m_visible = true; + bool m_showInTitleBar = true; bool m_hideWhenDisabled = false; public: using Super = TitleBarButtonType; - TitleBarButton(bool visible = true, QWidget *parent = nullptr); + TitleBarButton(bool showInTitleBar = true, QWidget *parent = nullptr); /** * Adjust this visibility change request with our internal settings: */ void setVisible(bool visible) override; + /** + * Configures, if the title bar button should be shown in title bar + */ + void setShowInTitleBar(bool show); + protected: /** * Handle EnabledChanged signal to set button invisible if the configured @@ -139,7 +144,7 @@ public: /** * Returns the button corresponding to the given title bar button identifier */ - QAbstractButton *button(eTitleBarButton which) const; + TitleBarButton *button(eTitleBarButton which) const; /** * Returns the auto hide title label, used when the dock area is expanded and auto hidden @@ -181,6 +186,11 @@ public: */ QString titleBarButtonToolTip(eTitleBarButton button) const; + /** + * Moves the dock area into its own floating widget if the area DockWidgetFloatable flag is true. + */ + void setAreaFloating(); + signals: /** * This signal is emitted if a tab in the tab bar is clicked by the user diff --git a/src/libs/advanceddockingsystem/dockareawidget.cpp b/src/libs/advanceddockingsystem/dockareawidget.cpp index 0a0fef497af..c4c74a38c1d 100644 --- a/src/libs/advanceddockingsystem/dockareawidget.cpp +++ b/src/libs/advanceddockingsystem/dockareawidget.cpp @@ -374,6 +374,7 @@ void DockAreaWidget::setAutoHideDockContainer(AutoHideDockContainer *autoHideDoc d->m_autoHideDockContainer = autoHideDockContainer; updateAutoHideButtonCheckState(); updateTitleBarButtonsToolTips(); + d->m_titleBar->button(TitleBarButtonAutoHide)->setShowInTitleBar(true); } void DockAreaWidget::addDockWidget(DockWidget *dockWidget) @@ -504,12 +505,7 @@ void DockAreaWidget::hideAreaWithNoVisibleContent() void DockAreaWidget::onTabCloseRequested(int index) { qCInfo(adsLog) << Q_FUNC_INFO << "index" << index; - auto *currentDockWidget = dockWidget(index); - if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose) - || currentDockWidget->features().testFlag(DockWidget::CustomCloseHandling)) - currentDockWidget->closeDockWidgetInternal(); - else - currentDockWidget->toggleView(false); + dockWidget(index)->requestCloseDockWidget(); } DockWidget *DockAreaWidget::currentDockWidget() const @@ -1075,27 +1071,33 @@ SideBarLocation DockAreaWidget::calculateSideTabBarArea() const return sideTab; } -void DockAreaWidget::setAutoHide(bool enable, SideBarLocation location) +void DockAreaWidget::setAutoHide(bool enable, SideBarLocation location, int tabIndex) { if (!isAutoHideFeatureEnabled()) return; if (!enable) { if (isAutoHide()) - autoHideDockContainer()->moveContentsToParent(); + d->m_autoHideDockContainer->moveContentsToParent(); return; } + // If this is already an auto hide container, then move it to new location. + if (isAutoHide()) { + d->m_autoHideDockContainer->moveToNewSideBarLocation(location, tabIndex); + return; + } + auto area = (SideBarNone == location) ? calculateSideTabBarArea() : location; - for (const auto DockWidget : openedDockWidgets()) { + for (const auto dockWidget : openedDockWidgets()) { if (enable == isAutoHide()) continue; - if (!DockWidget->features().testFlag(DockWidget::DockWidgetPinnable)) + if (!dockWidget->features().testFlag(DockWidget::DockWidgetPinnable)) continue; - dockContainer()->createAndSetupAutoHideContainer(area, DockWidget); + dockContainer()->createAndSetupAutoHideContainer(area, dockWidget, tabIndex++); } } @@ -1112,6 +1114,11 @@ void DockAreaWidget::closeOtherAreas() dockContainer()->closeOtherAreas(this); } +void DockAreaWidget::setFloating() +{ + d->m_titleBar->setAreaFloating(); +} + DockAreaTitleBar *DockAreaWidget::titleBar() const { return d->m_titleBar; diff --git a/src/libs/advanceddockingsystem/dockareawidget.h b/src/libs/advanceddockingsystem/dockareawidget.h index ca23226fdbe..59f8b9ee207 100644 --- a/src/libs/advanceddockingsystem/dockareawidget.h +++ b/src/libs/advanceddockingsystem/dockareawidget.h @@ -369,7 +369,7 @@ public: * If the dock area is switched to auto hide mode, then all dock widgets * that are pinable will be added to the sidebar */ - void setAutoHide(bool enable, SideBarLocation location = SideBarNone); + void setAutoHide(bool enable, SideBarLocation location = SideBarNone, int tabIndex = -1); /** * Switches the dock area to auto hide mode or vice versa depending on its @@ -382,6 +382,11 @@ public: */ void closeOtherAreas(); + /** + * Moves the dock area into its own floating widget if the area DockWidgetFloatable flag is true. + */ + void setFloating(); + signals: /** * This signal is emitted when user clicks on a tab at an index. diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp index dcfb7964366..2c0d306ea74 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp @@ -84,7 +84,7 @@ public: QList m_autoHideWidgets; QMap m_sideTabBarWidgets; QGridLayout *m_layout = nullptr; - QSplitter *m_rootSplitter = nullptr; + DockSplitter *m_rootSplitter = nullptr; bool m_isFloating = false; DockAreaWidget *m_lastAddedAreaCache[5]; int m_visibleDockAreaCount = -1; @@ -122,34 +122,51 @@ public: */ void dropIntoContainer(FloatingDockContainer *floatingWidget, DockWidgetArea area); + /** + * Drop floating widget into auto hide side bar + */ + void dropIntoAutoHideSideBar(FloatingDockContainer *floatingWidget, DockWidgetArea area); + + /** + * Creates a new tab for a widget dropped into the center of a section + */ + void dropIntoCenterOfSection(FloatingDockContainer *floatingWidget, + DockAreaWidget *targetArea, + int tabIndex); + /** * Drop floating widget into dock area */ void dropIntoSection(FloatingDockContainer *floatingWidget, DockAreaWidget *targetArea, - DockWidgetArea area); + DockWidgetArea area, + int tabIndex = 0); /** - * Moves the dock widget or dock area given in Widget parameter to a - * new dock widget area + * Moves the dock widget or dock area given in Widget parameter to a new dock widget area. */ - void moveToNewSection(QWidget *widget, DockAreaWidget *targetArea, DockWidgetArea area); + void moveToNewSection(QWidget *widget, + DockAreaWidget *targetArea, + DockWidgetArea area, + int tabIndex = 0); /** - * Moves the dock widget or dock area given in Widget parameter to a - * a dock area in container + * Moves the dock widget or dock area given in Widget parameter to a dock area in container. */ void moveToContainer(QWidget *widget, DockWidgetArea area); /** * Creates a new tab for a widget dropped into the center of a section */ - void dropIntoCenterOfSection(FloatingDockContainer *floatingWidget, DockAreaWidget *targetArea); + void moveIntoCenterOfSection(QWidget *widget, DockAreaWidget *targetArea, int tabIndex = 0); /** - * Creates a new tab for a widget dropped into the center of a section + * Moves the dock widget or dock area given in Widget parameter to + * a auto hide sidebar area */ - void moveIntoCenterOfSection(QWidget *widget, DockAreaWidget *targetArea); + void moveToAutoHideSideBar(QWidget *widget, + DockWidgetArea area, + int tabIndex = TabDefaultInsertIndex); /** * Adds new dock areas to the internal dock area list @@ -371,12 +388,12 @@ void DockContainerWidgetPrivate::dropIntoContainer(FloatingDockContainer *floati auto newDockAreas = floatingDockContainer->findChildren(QString(), Qt::FindChildrenRecursively); - QSplitter *splitter = m_rootSplitter; + auto *splitter = m_rootSplitter; if (m_dockAreas.count() <= 1) { splitter->setOrientation(insertParam.orientation()); } else if (splitter->orientation() != insertParam.orientation()) { - QSplitter *newSplitter = createSplitter(insertParam.orientation()); + auto *newSplitter = createSplitter(insertParam.orientation()); QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter); newSplitter->addWidget(splitter); updateSplitterHandles(newSplitter); @@ -411,42 +428,58 @@ void DockContainerWidgetPrivate::dropIntoContainer(FloatingDockContainer *floati q->dumpLayout(); } +void DockContainerWidgetPrivate::dropIntoAutoHideSideBar(FloatingDockContainer *floatingWidget, + DockWidgetArea area) +{ + auto sideBarLocation = internal::toSideBarLocation(area); + auto newDockAreas = floatingWidget->findChildren(QString(), + Qt::FindChildrenRecursively); + int tabIndex = m_dockManager->containerOverlay()->tabIndexUnderCursor(); + for (auto dockArea : newDockAreas) { + auto dockWidgets = dockArea->dockWidgets(); + for (auto dockWidget : dockWidgets) + q->createAndSetupAutoHideContainer(sideBarLocation, dockWidget, tabIndex++); + } +} + void DockContainerWidgetPrivate::dropIntoCenterOfSection(FloatingDockContainer *floatingWidget, - DockAreaWidget *targetArea) + DockAreaWidget *targetArea, + int tabIndex) { DockContainerWidget *floatingContainer = floatingWidget->dockContainer(); auto newDockWidgets = floatingContainer->dockWidgets(); auto topLevelDockArea = floatingContainer->topLevelDockArea(); int newCurrentIndex = -1; + tabIndex = qMax(0, tabIndex); - // If the floating widget contains only one single dock are, then the - // current dock widget of the dock area will also be the future current - // dock widget in the drop area. + // If the floating widget contains only one single dock are, then the current dock widget of + // the dock area will also be the future current dock widget in the drop area. if (topLevelDockArea) newCurrentIndex = topLevelDockArea->currentIndex(); for (int i = 0; i < newDockWidgets.count(); ++i) { DockWidget *dockWidget = newDockWidgets[i]; + targetArea->insertDockWidget(tabIndex + i, dockWidget, false); targetArea->insertDockWidget(i, dockWidget, false); - // If the floating widget contains multiple visible dock areas, then we - // simply pick the first visible open dock widget and make it - // the current one. + // If the floating widget contains multiple visible dock areas, then we simply pick the + // first visible open dock widget and make it the current one. if (newCurrentIndex < 0 && !dockWidget->isClosed()) newCurrentIndex = i; } - targetArea->setCurrentIndex(newCurrentIndex); + targetArea->setCurrentIndex(newCurrentIndex + tabIndex); targetArea->updateTitleBarVisibility(); return; } void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floatingWidget, DockAreaWidget *targetArea, - DockWidgetArea area) + DockWidgetArea area, + int tabIndex) { - // Dropping into center means all dock widgets in the dropped floating - // widget will become tabs of the drop area + // Dropping into center means all dock widgets in the dropped floating widget will become + // tabs of the drop area. if (CenterDockWidgetArea == area) { - dropIntoCenterOfSection(floatingWidget, targetArea); + dropIntoCenterOfSection(floatingWidget, targetArea, tabIndex); return; } @@ -525,44 +558,15 @@ void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floating q->dumpLayout(); } -void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget *widget, DockAreaWidget *targetArea) -{ - auto droppedDockWidget = qobject_cast(widget); - auto droppedArea = qobject_cast(widget); - - if (droppedDockWidget) { - DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget(); - if (oldDockArea == targetArea) - return; - - if (oldDockArea) - oldDockArea->removeDockWidget(droppedDockWidget); - - targetArea->insertDockWidget(0, droppedDockWidget, true); - } else { - QList newDockWidgets = droppedArea->dockWidgets(); - int newCurrentIndex = droppedArea->currentIndex(); - for (int i = 0; i < newDockWidgets.count(); ++i) { - DockWidget *dockWidget = newDockWidgets[i]; - targetArea->insertDockWidget(i, dockWidget, false); - } - targetArea->setCurrentIndex(newCurrentIndex); - droppedArea->dockContainer()->removeDockArea(droppedArea); - droppedArea->deleteLater(); - } - - targetArea->updateTitleBarVisibility(); - return; -} - void DockContainerWidgetPrivate::moveToNewSection(QWidget *widget, DockAreaWidget *targetArea, - DockWidgetArea area) + DockWidgetArea area, + int tabIndex) { - // Dropping into center means all dock widgets in the dropped floating - // widget will become tabs of the drop area + // Dropping into center means all dock widgets in the dropped floating widget will become + // tabs of the drop area. if (CenterDockWidgetArea == area) { - moveIntoCenterOfSection(widget, targetArea); + moveIntoCenterOfSection(widget, targetArea, tabIndex); return; } @@ -612,31 +616,6 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget *widget, addDockAreasToList({newDockArea}); } -void DockContainerWidgetPrivate::updateSplitterHandles(QSplitter *splitter) -{ - if (!m_dockManager->centralWidget() || !splitter) - return; - - for (int i = 0; i < splitter->count(); ++i) - splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0); -} - -bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget *widget) -{ - if (!m_dockManager->centralWidget()) - return true; - - auto area = qobject_cast(widget); - if (area) - return area->isCentralWidgetArea(); - - auto innerSplitter = qobject_cast(widget); - if (innerSplitter) - return innerSplitter->isResizingWithContainer(); - - return false; -} - void DockContainerWidgetPrivate::moveToContainer(QWidget *widget, DockWidgetArea area) { DockWidget *droppedDockWidget = qobject_cast(widget); @@ -671,6 +650,92 @@ void DockContainerWidgetPrivate::moveToContainer(QWidget *widget, DockWidgetArea m_lastAddedAreaCache[areaIdToIndex(area)] = newDockArea; } +void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget *widget, + DockAreaWidget *targetArea, + int tabIndex) +{ + auto droppedDockWidget = qobject_cast(widget); + auto droppedArea = qobject_cast(widget); + tabIndex = qMax(0, tabIndex); + + if (droppedDockWidget) { + DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget(); + if (oldDockArea == targetArea) + return; + + if (oldDockArea) + oldDockArea->removeDockWidget(droppedDockWidget); + + targetArea->insertDockWidget(tabIndex, droppedDockWidget, true); + } else { + QList newDockWidgets = droppedArea->dockWidgets(); + int newCurrentIndex = droppedArea->currentIndex(); + for (int i = 0; i < newDockWidgets.count(); ++i) { + DockWidget *dockWidget = newDockWidgets[i]; + targetArea->insertDockWidget(tabIndex + i, dockWidget, false); + } + targetArea->setCurrentIndex(tabIndex + newCurrentIndex); + droppedArea->dockContainer()->removeDockArea(droppedArea); + droppedArea->deleteLater(); + } + + targetArea->updateTitleBarVisibility(); + return; +} + +void DockContainerWidgetPrivate::moveToAutoHideSideBar(QWidget *widget, + DockWidgetArea area, + int tabIndex) +{ + DockWidget *droppedDockWidget = qobject_cast(widget); + DockAreaWidget *droppedDockArea = qobject_cast(widget); + auto sideBarLocation = internal::toSideBarLocation(area); + + if (droppedDockWidget) { + if (q == droppedDockWidget->dockContainer()) + droppedDockWidget->setAutoHide(true, sideBarLocation, tabIndex); + else + q->createAndSetupAutoHideContainer(sideBarLocation, droppedDockWidget, tabIndex); + + } else { + if (q == droppedDockArea->dockContainer()) { + droppedDockArea->setAutoHide(true, sideBarLocation, tabIndex); + } else { + for (const auto dockWidget : droppedDockArea->openedDockWidgets()) { + if (!dockWidget->features().testFlag(DockWidget::DockWidgetPinnable)) + continue; + + q->createAndSetupAutoHideContainer(sideBarLocation, dockWidget, tabIndex++); + } + } + } +} + +void DockContainerWidgetPrivate::updateSplitterHandles(QSplitter *splitter) +{ + if (!m_dockManager->centralWidget() || !splitter) + return; + + for (int i = 0; i < splitter->count(); ++i) + splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0); +} + +bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget *widget) +{ + if (!m_dockManager->centralWidget()) + return true; + + auto area = qobject_cast(widget); + if (area) + return area->isCentralWidgetArea(); + + auto innerSplitter = qobject_cast(widget); + if (innerSplitter) + return innerSplitter->isResizingWithContainer(); + + return false; +} + void DockContainerWidgetPrivate::addDockAreasToList(const QList newDockAreas) { const int countBefore = m_dockAreas.count(); @@ -740,7 +805,7 @@ void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter &stream, Q void DockContainerWidgetPrivate::saveAutoHideWidgetsState(QXmlStreamWriter &s) { for (const auto sideTabBar : m_sideTabBarWidgets.values()) { - if (!sideTabBar->tabCount()) + if (!sideTabBar->count()) continue; sideTabBar->saveState(s); @@ -896,11 +961,11 @@ bool DockContainerWidgetPrivate::restoreSideBar(DockingStateReader &stateReader, if (!dockWidget || testing) continue; - auto sideBar = q->sideTabBar(area); + auto sideBar = q->autoHideSideBar(area); AutoHideDockContainer *autoHideContainer; if (dockWidget->isAutoHide()) { autoHideContainer = dockWidget->autoHideDockContainer(); - if (autoHideContainer->sideBar() != sideBar) + if (autoHideContainer->autoHideSideBar() != sideBar) sideBar->addAutoHideWidget(autoHideContainer); } else { autoHideContainer = sideBar->insertDockWidget(-1, dockWidget); @@ -931,7 +996,7 @@ void DockContainerWidgetPrivate::addDockArea(DockAreaWidget *newDockArea, DockWi if (m_dockAreas.count() <= 1) m_rootSplitter->setOrientation(insertParam.orientation()); - QSplitter *splitter = m_rootSplitter; + auto *splitter = m_rootSplitter; if (splitter->orientation() == insertParam.orientation()) { insertWidgetIntoSplitter(splitter, newDockArea, insertParam.append()); updateSplitterHandles(splitter); @@ -939,7 +1004,7 @@ void DockContainerWidgetPrivate::addDockArea(DockAreaWidget *newDockArea, DockWi splitter->show(); } else { - QSplitter *newSplitter = createSplitter(insertParam.orientation()); + auto *newSplitter = createSplitter(insertParam.orientation()); if (insertParam.append()) { QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter); newSplitter->addWidget(splitter); @@ -1108,7 +1173,8 @@ DockAreaWidget *DockContainerWidget::addDockWidget(DockWidgetArea area, } AutoHideDockContainer *DockContainerWidget::createAndSetupAutoHideContainer(SideBarLocation area, - DockWidget *dockWidget) + DockWidget *dockWidget, + int tabIndex) { if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) { Q_ASSERT_X(false, @@ -1121,7 +1187,7 @@ AutoHideDockContainer *DockContainerWidget::createAndSetupAutoHideContainer(Side d->m_dockManager); // Auto hide Dock Container needs a valid dock manager } - return sideTabBar(area)->insertDockWidget(-1, dockWidget); + return autoHideSideBar(area)->insertDockWidget(tabIndex, dockWidget); } void DockContainerWidget::removeDockWidget(DockWidget *dockWidget) @@ -1210,7 +1276,7 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) } QWidget *widget = splitter->widget(0); - QSplitter *childSplitter = qobject_cast(widget); + auto *childSplitter = qobject_cast(widget); // If the one and only content widget of the splitter is not a splitter // then we are finished if (!childSplitter) { @@ -1227,7 +1293,7 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) qCInfo(adsLog) << "RootSplitter replaced by child splitter"; } else if (splitter->count() == 1) { qCInfo(adsLog) << "Replacing splitter with content"; - QSplitter *parentSplitter = internal::findParent(splitter); + auto *parentSplitter = internal::findParent(splitter); auto sizes = parentSplitter->sizes(); QWidget *widget = splitter->widget(0); widget->setParent(this); @@ -1299,11 +1365,12 @@ void DockContainerWidget::dropFloatingWidget(FloatingDockContainer *floatingWidg qCInfo(adsLog) << Q_FUNC_INFO; DockWidget *singleDroppedDockWidget = floatingWidget->topLevelDockWidget(); DockWidget *singleDockWidget = topLevelDockWidget(); - DockAreaWidget *dockArea = dockAreaAt(targetPosition); auto dropArea = InvalidDockWidgetArea; auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor(); bool dropped = false; + DockAreaWidget *dockArea = dockAreaAt(targetPosition); + // Mouse is over dock area if (dockArea) { auto dropOverlay = d->m_dockManager->dockAreaOverlay(); dropOverlay->setAllowedAreas(dockArea->allowedAreas()); @@ -1313,24 +1380,27 @@ void DockContainerWidget::dropFloatingWidget(FloatingDockContainer *floatingWidg if (dropArea != InvalidDockWidgetArea) { qCInfo(adsLog) << "Dock Area Drop Content: " << dropArea; - d->dropIntoSection(floatingWidget, dockArea, dropArea); + int tabIndex = d->m_dockManager->dockAreaOverlay()->tabIndexUnderCursor(); + d->dropIntoSection(floatingWidget, dockArea, dropArea, tabIndex); dropped = true; } } - // mouse is over container - if (InvalidDockWidgetArea == dropArea) { - dropArea = containerDropArea; - qCInfo(adsLog) << "Container Drop Content: " << dropArea; - if (dropArea != InvalidDockWidgetArea) { - d->dropIntoContainer(floatingWidget, dropArea); - dropped = true; - } + // Mouse is over container or auto hide side bar + if (InvalidDockWidgetArea == dropArea && InvalidDockWidgetArea != containerDropArea) { + qCInfo(adsLog) << "Container Drop Content: " << containerDropArea; + + if (internal::isSideBarArea(containerDropArea)) + d->dropIntoAutoHideSideBar(floatingWidget, containerDropArea); + else + d->dropIntoContainer(floatingWidget, containerDropArea); + + dropped = true; } // Remove the auto hide widgets from the FloatingWidget and insert them into this widget for (auto autohideWidget : floatingWidget->dockContainer()->autoHideWidgets()) { - auto sideBar = sideTabBar(autohideWidget->sideBarLocation()); + auto sideBar = autoHideSideBar(autohideWidget->sideBarLocation()); sideBar->addAutoHideWidget(autohideWidget); } @@ -1356,11 +1426,14 @@ void DockContainerWidget::dropFloatingWidget(FloatingDockContainer *floatingWidg void DockContainerWidget::dropWidget(QWidget *widget, DockWidgetArea dropArea, - DockAreaWidget *targetAreaWidget) + DockAreaWidget *targetAreaWidget, + int tabIndex) { DockWidget *singleDockWidget = topLevelDockWidget(); if (targetAreaWidget) - d->moveToNewSection(widget, targetAreaWidget, dropArea); + d->moveToNewSection(widget, targetAreaWidget, dropArea, tabIndex); + else if (internal::isSideBarArea(dropArea)) + d->moveToAutoHideSideBar(widget, dropArea, tabIndex); else d->moveToContainer(widget, dropArea); @@ -1466,7 +1539,7 @@ bool DockContainerWidget::restoreState(DockingStateReader &stateReader, bool tes d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter); QSplitter *oldRoot = d->m_rootSplitter; - d->m_rootSplitter = qobject_cast(newRootSplitter); + d->m_rootSplitter = qobject_cast(newRootSplitter); oldRoot->deleteLater(); return true; @@ -1621,7 +1694,7 @@ void DockContainerWidget::closeOtherAreas(DockAreaWidget *keepOpenArea) } } -AutoHideSideBar *DockContainerWidget::sideTabBar(SideBarLocation area) const +AutoHideSideBar *DockContainerWidget::autoHideSideBar(SideBarLocation area) const { return d->m_sideTabBarWidgets[area]; } @@ -1639,7 +1712,17 @@ QRect DockContainerWidget::contentRectGlobal() const if (!d->m_rootSplitter) return QRect(); - return internal::globalGeometry(d->m_rootSplitter); + if (d->m_rootSplitter->hasVisibleContent()) { + return d->m_rootSplitter->geometry(); + } else { + auto contentRect = rect(); + contentRect.adjust(autoHideSideBar(SideBarLeft)->sizeHint().width(), + autoHideSideBar(SideBarTop)->sizeHint().height(), + -autoHideSideBar(SideBarRight)->sizeHint().width(), + -autoHideSideBar(SideBarBottom)->sizeHint().height()); + + return contentRect; + } } DockManager *DockContainerWidget::dockManager() const diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.h b/src/libs/advanceddockingsystem/dockcontainerwidget.h index 58b9342cf52..f2c6085a1d3 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.h +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.h @@ -74,7 +74,8 @@ protected: * Returns nullptr if you try and insert into an area where the configuration is not enabled */ AutoHideDockContainer *createAndSetupAutoHideContainer(SideBarLocation area, - DockWidget *dockWidget); + DockWidget *dockWidget, + int tabIndex = -1); /** * Helper function for creation of the root splitter @@ -98,7 +99,10 @@ protected: * a nullptr, then the DropArea indicates the drop area in the given * TargetAreaWidget */ - void dropWidget(QWidget *widget, DockWidgetArea dropArea, DockAreaWidget *targetAreaWidget); + void dropWidget(QWidget *widget, + DockWidgetArea dropArea, + DockAreaWidget *targetAreaWidget, + int tabIndex = -1); /** * Adds the given dock area to this container widget @@ -299,7 +303,7 @@ public: /** * Returns the side tab widget for the given area */ - AutoHideSideBar *sideTabBar(SideBarLocation area) const; + AutoHideSideBar *autoHideSideBar(SideBarLocation area) const; /** * Access function for auto hide widgets diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index f0e9a8d0e1e..195f634023b 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -48,6 +48,8 @@ struct DockAreaWidgetPrivate; class IconProvider; class DockFocusController; class AutoHideSideBar; +class AutoHideTab; +struct AutoHideTabPrivate; inline constexpr QStringView workspaceFolderName{u"workspaces"}; inline constexpr QStringView workspaceFileExtension{u"wrk"}; @@ -84,6 +86,8 @@ private: friend class DockAreaTitleBar; friend class AutoHideDockContainer; friend AutoHideSideBar; + friend AutoHideTab; + friend AutoHideTabPrivate; public: using Super = DockContainerWidget; @@ -182,8 +186,8 @@ public: = 0x40, ///< Close button of an auto hide container collapses the dock instead of hiding it completely DefaultAutoHideConfig - = AutoHideFeatureEnabled - | DockAreaHasAutoHideButton ///< the default configuration for left and right side bars + = AutoHideFeatureEnabled | DockAreaHasAutoHideButton + | AutoHideCloseButtonCollapsesDock ///< the default configuration for left and right side bars }; Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag) diff --git a/src/libs/advanceddockingsystem/dockoverlay.cpp b/src/libs/advanceddockingsystem/dockoverlay.cpp index 8ca418b645f..cce1a006fd0 100644 --- a/src/libs/advanceddockingsystem/dockoverlay.cpp +++ b/src/libs/advanceddockingsystem/dockoverlay.cpp @@ -3,8 +3,12 @@ #include "dockoverlay.h" +#include "autohidesidebar.h" +#include "dockareatabbar.h" #include "dockareatitlebar.h" #include "dockareawidget.h" +#include "dockcontainerwidget.h" +#include "dockmanager.h" #include @@ -25,6 +29,10 @@ namespace ADS { +static const int g_autoHideAreaWidth = 32; +static const int g_autoHideAreaMouseZone = 8; +static const int g_invalidTabIndex = -2; + /** * Private data class of DockOverlay */ @@ -39,6 +47,7 @@ public: bool m_dropPreviewEnabled = true; DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay; QRect m_dropAreaRect; + int m_tabIndex = g_invalidTabIndex; /** * Private data constructor @@ -46,6 +55,16 @@ public: DockOverlayPrivate(DockOverlay *parent) : q(parent) {} + + /** + * Returns the overlay width / height depending on the visibility of the sidebar. + */ + int sideBarOverlaySize(SideBarLocation sideBarLocation); + + /** + * The area where the mouse is considered in the sidebar. + */ + int sideBarMouseZone(SideBarLocation sideBarLocation); }; /** @@ -132,7 +151,15 @@ public: label->setObjectName("DockWidgetAreaLabel"); const qreal metric = dropIndicatorWidth(label); - const QSizeF size(metric, metric); + QSizeF size(metric, metric); + + if (internal::isSideBarArea(dockWidgetArea)) { + auto sideBarLocation = internal::toSideBarLocation(dockWidgetArea); + if (internal::isHorizontalSideBarLocation(sideBarLocation)) + size.setHeight(size.height() / 2); + else + size.setWidth(size.width() / 2); + } label->setPixmap(createHighDpiDropIndicatorPixmap(size, dockWidgetArea, mode)); label->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); @@ -159,6 +186,11 @@ public: { const QColor borderColor = iconColor(DockOverlayCross::FrameColor); const QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor); + + QColor overlayColor = iconColor(DockOverlayCross::OverlayColor); + if (overlayColor.alpha() == 255) + overlayColor.setAlpha(64); + const double devicePixelRatio = q->window()->devicePixelRatioF(); const QSizeF pixmapSize = size * devicePixelRatio; QPixmap pixmap(pixmapSize.toSize()); @@ -223,19 +255,18 @@ public: } const QSizeF baseSize = baseRect.size(); - if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) { + bool isOuterContainerArea = (DockOverlay::ModeContainerOverlay == mode) + && (dockWidgetArea != CenterDockWidgetArea) + && !internal::isSideBarArea(dockWidgetArea); + if (isOuterContainerArea) baseRect = areaRect; - } painter.fillRect(baseRect, backgroundColor); + if (areaRect.isValid()) { pen = painter.pen(); pen.setColor(borderColor); - QColor color = iconColor(DockOverlayCross::OverlayColor); - if (color.alpha() == 255) { - color.setAlpha(64); - } - painter.setBrush(color); + painter.setBrush(overlayColor); painter.setPen(Qt::NoPen); painter.drawRect(areaRect); @@ -264,7 +295,7 @@ public: painter.restore(); // Draw arrow for outer container drop indicators - if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) { + if (isOuterContainerArea) { QRectF arrowRect; arrowRect.setSize(baseSize); arrowRect.setWidth(arrowRect.width() / 4.6); @@ -302,6 +333,26 @@ public: } }; // class DockOverlayCrossPrivate +int DockOverlayPrivate::sideBarOverlaySize(SideBarLocation sideBarLocation) +{ + auto container = qobject_cast(m_targetWidget.data()); + auto sideBar = container->autoHideSideBar(sideBarLocation); + if (!sideBar || !sideBar->isVisibleTo(container)) + return g_autoHideAreaWidth; + else + return (sideBar->orientation() == Qt::Horizontal) ? sideBar->height() : sideBar->width(); +} + +int DockOverlayPrivate::sideBarMouseZone(SideBarLocation sideBarLocation) +{ + auto container = qobject_cast(m_targetWidget.data()); + auto sideBar = container->autoHideSideBar(sideBarLocation); + if (!sideBar || !sideBar->isVisibleTo(container)) + return g_autoHideAreaMouseZone; + else + return (sideBar->orientation() == Qt::Horizontal) ? sideBar->height() : sideBar->width(); +} + DockOverlay::DockOverlay(QWidget *parent, eMode mode) : QFrame(parent) , d(new DockOverlayPrivate(this)) @@ -338,6 +389,14 @@ void DockOverlay::setAllowedAreas(DockWidgetAreas areas) d->m_cross->reset(); } +void DockOverlay::setAllowedArea(DockWidgetArea area, bool enable) +{ + auto areasOld = d->m_allowedAreas; + d->m_allowedAreas.setFlag(area, enable); + if (areasOld != d->m_allowedAreas) + d->m_cross->reset(); +} + DockWidgetAreas DockOverlay::allowedAreas() const { return d->m_allowedAreas; @@ -345,21 +404,62 @@ DockWidgetAreas DockOverlay::allowedAreas() const DockWidgetArea DockOverlay::dropAreaUnderCursor() const { + d->m_tabIndex = g_invalidTabIndex; + if (!d->m_targetWidget) + return InvalidDockWidgetArea; + DockWidgetArea result = d->m_cross->cursorLocation(); if (result != InvalidDockWidgetArea) return result; - DockAreaWidget *dockArea = qobject_cast(d->m_targetWidget.data()); - if (!dockArea) + auto cursorPos = QCursor::pos(); + auto dockArea = qobject_cast(d->m_targetWidget.data()); + if (!dockArea + && DockManager::autoHideConfigFlags().testFlag(DockManager::AutoHideFeatureEnabled)) { + auto rectangle = rect(); + const QPoint pos = mapFromGlobal(QCursor::pos()); + if ((pos.x() < d->sideBarMouseZone(SideBarLeft)) + && d->m_allowedAreas.testFlag(LeftAutoHideArea)) { + result = LeftAutoHideArea; + } else if (pos.x() > (rectangle.width() - d->sideBarMouseZone(SideBarRight)) + && d->m_allowedAreas.testFlag(RightAutoHideArea)) { + result = RightAutoHideArea; + } else if (pos.y() < d->sideBarMouseZone(SideBarTop) + && d->m_allowedAreas.testFlag(TopAutoHideArea)) { + result = TopAutoHideArea; + } else if (pos.y() > (rectangle.height() - d->sideBarMouseZone(SideBarBottom)) + && d->m_allowedAreas.testFlag(BottomAutoHideArea)) { + result = BottomAutoHideArea; + } + + auto sideBarLocation = internal::toSideBarLocation(result); + if (sideBarLocation != SideBarNone) { + auto Container = qobject_cast(d->m_targetWidget.data()); + auto SideBar = Container->autoHideSideBar(sideBarLocation); + if (SideBar->isVisible()) { + d->m_tabIndex = SideBar->tabInsertIndexAt(SideBar->mapFromGlobal(cursorPos)); + } + } return result; + } else if (!dockArea) { + return result; + } if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea) && !dockArea->titleBar()->isHidden() - && dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos()))) + && dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(cursorPos))) { + auto tabBar = dockArea->titleBar()->tabBar(); + d->m_tabIndex = tabBar->tabInsertIndexAt(tabBar->mapFromGlobal(cursorPos)); return CenterDockWidgetArea; + } return result; } +int DockOverlay::tabIndexUnderCursor() const +{ + return d->m_tabIndex; +} + DockWidgetArea DockOverlay::visibleDropAreaUnderCursor() const { if (isHidden() || !d->m_dropPreviewEnabled) @@ -416,6 +516,7 @@ bool DockOverlay::dropPreviewEnabled() const void DockOverlay::paintEvent(QPaintEvent *event) { Q_UNUSED(event) + // Draw rect based on location if (!d->m_dropPreviewEnabled) { d->m_dropAreaRect = QRect(); @@ -442,9 +543,22 @@ void DockOverlay::paintEvent(QPaintEvent *event) case CenterDockWidgetArea: rectangle = rect(); break; + case LeftAutoHideArea: + rectangle.setWidth(d->sideBarOverlaySize(SideBarLeft)); + break; + case RightAutoHideArea: + rectangle.setX(rectangle.width() - d->sideBarOverlaySize(SideBarRight)); + break; + case TopAutoHideArea: + rectangle.setHeight(d->sideBarOverlaySize(SideBarTop)); + break; + case BottomAutoHideArea: + rectangle.setY(rectangle.height() - d->sideBarOverlaySize(SideBarBottom)); + break; default: return; } + QPainter painter(this); QColor color = palette().color(QPalette::Active, QPalette::Highlight); QPen pen = painter.pen(); diff --git a/src/libs/advanceddockingsystem/dockoverlay.h b/src/libs/advanceddockingsystem/dockoverlay.h index 2dd6b7aef46..0b3c82d941b 100644 --- a/src/libs/advanceddockingsystem/dockoverlay.h +++ b/src/libs/advanceddockingsystem/dockoverlay.h @@ -52,11 +52,26 @@ public: */ DockWidgetAreas allowedAreas() const; + /** + * Enable / disable a certain area + */ + void setAllowedArea(DockWidgetArea area, bool enable); + /** * Returns the drop area under the current cursor location */ DockWidgetArea dropAreaUnderCursor() const; + /** + * If the drop area is the CenterDockWidgetArea or a sidebar area, then this function returns + * the index of the tab under cursor. Call this function after call to dropAreaUnderCursor() + * because this function updates the tab index. + * A value of -1 indicates a position before the first tab and a value of tabCount() indicates + * a position behind the last tab. + * A value of -2 indicates an valid value + */ + int tabIndexUnderCursor() const; + /** * This function returns the same like dropAreaUnderCursor() if this * overlay is not hidden and if drop preview is enabled and returns diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index d0127cb8087..229995b9322 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -409,6 +409,11 @@ bool DockWidget::isAutoHide() const return !d->m_sideTabWidget.isNull(); } +SideBarLocation DockWidget::autoHideLocation() const +{ + return isAutoHide() ? autoHideDockContainer()->sideBarLocation() : SideBarNone; +} + bool DockWidget::isFloating() const { if (!isInFloatingContainer()) @@ -746,7 +751,10 @@ void DockWidget::setFloating() if (isClosed()) return; - d->m_tabWidget->detachDockWidget(); + if (isAutoHide()) + dockAreaWidget()->setFloating(); + else + d->m_tabWidget->detachDockWidget(); } void DockWidget::deleteDockWidget() @@ -764,6 +772,15 @@ void DockWidget::closeDockWidget() closeDockWidgetInternal(true); } +void DockWidget::requestCloseDockWidget() +{ + if (features().testFlag(DockWidget::DockWidgetDeleteOnClose) + || features().testFlag(DockWidget::CustomCloseHandling)) + closeDockWidgetInternal(false); + else + toggleView(false); +} + bool DockWidget::closeDockWidgetInternal(bool forceClose) { if (!forceClose) @@ -859,21 +876,24 @@ void DockWidget::raise() } } -void DockWidget::setAutoHide(bool enable, SideBarLocation location) +void DockWidget::setAutoHide(bool enable, SideBarLocation location, int tabIndex) { if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) return; // Do nothing if nothing changes - if (enable == isAutoHide()) + if (enable == isAutoHide() && location == autoHideLocation()) return; auto dockArea = dockAreaWidget(); + if (!enable) { dockArea->setAutoHide(false); + } else if (isAutoHide()) { + autoHideDockContainer()->moveToNewSideBarLocation(location); } else { auto area = (SideBarNone == location) ? dockArea->calculateSideTabBarArea() : location; - dockContainer()->createAndSetupAutoHideContainer(area, this); + dockContainer()->createAndSetupAutoHideContainer(area, this, tabIndex); } } diff --git a/src/libs/advanceddockingsystem/dockwidget.h b/src/libs/advanceddockingsystem/dockwidget.h index 30a1e190794..4f9d13be290 100644 --- a/src/libs/advanceddockingsystem/dockwidget.h +++ b/src/libs/advanceddockingsystem/dockwidget.h @@ -338,16 +338,20 @@ public: bool isAutoHide() const; /** - * Returns the auto hide dock container of this dock widget - * or 0 if there is none + * Returns the auto hide dock container of this dock widget or 0 if there is none. */ AutoHideDockContainer *autoHideDockContainer() const; + /** + * Returns the auto hide side bar location or SideBarNone if, this is not an autohide dock widget. + */ + SideBarLocation autoHideLocation() const; + /** * This property holds whether the dock widget is floating. - * A dock widget is only floating, if it is the one and only widget inside - * of a floating container. If there are more than one dock widget in a - * floating container, the all dock widgets are docked and not floating. + * A dock widget is only floating, if it is the one and only widget inside of a floating + * container. If there are more than one dock widget in a floating container, the all dock + * widgets are docked and not floating. */ bool isFloating() const; @@ -546,23 +550,28 @@ public: // reimplements QFrame */ void closeDockWidget(); + /** + * Request closing of the dock widget. + * For DockWidget with default close handling, the function does the same like clodeDockWidget() + * but if the flag CustomCloseHandling is set, the function only emits the closeRequested() signal. + */ + void requestCloseDockWidget(); + /** * Shows the widget in full-screen mode. - * Normally this function only affects windows. To make the interface - * compatible to QDockWidget, this function also maximizes a floating - * dock widget. + * Normally this function only affects windows. To make the interface compatible to QDockWidget, + * this function also maximizes a floating dock widget. * - * \note Full-screen mode works fine under Windows, but has certain - * problems (doe not work) under X (Linux). These problems are due to - * limitations of the ICCCM protocol that specifies the communication - * between X11 clients and the window manager. ICCCM simply does not + * \note Full-screen mode works fine under Windows, but has certain problems (doe not work) + * under X (Linux). These problems are due to limitations of the ICCCM protocol that specifies + * the communication between X11 clients and the window manager. ICCCM simply does not * understand the concept of non-decorated full-screen windows. */ void showFullScreen(); /** - * This function complements showFullScreen() to restore the widget - * after it has been in full screen mode. + * This function complements showFullScreen() to restore the widget after it has been in full + * screen mode. */ void showNormal(); @@ -570,11 +579,10 @@ public: // reimplements QFrame * Sets the dock widget into auto hide mode if this feature is enabled * via CDockManager::setAutoHideFlags(CDockManager::AutoHideFeatureEnabled) */ - void setAutoHide(bool enable, SideBarLocation location = SideBarNone); + void setAutoHide(bool enable, SideBarLocation location = SideBarNone, int tabIndex = -1); /** - * Switches the dock widget to auto hide mode or vice versa depending on its - * current state. + * Switches the dock widget to auto hide mode or vice versa depending on its current state. */ void toggleAutoHide(SideBarLocation location = SideBarNone); diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index 091bfe6509f..4efa339e34e 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -58,7 +58,6 @@ public: TabButton *m_closeButton = nullptr; QPoint m_tabDragStartPosition; QSize m_iconSize; - bool m_mousePressed = false; /** * Private data constructor @@ -340,7 +339,6 @@ void DockWidgetTab::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { event->accept(); - d->m_mousePressed = true; d->saveDragStartMousePosition(event->globalPosition().toPoint()); d->m_dragState = DraggingMousePressed; if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) { @@ -356,7 +354,6 @@ void DockWidgetTab::mousePressEvent(QMouseEvent *event) void DockWidgetTab::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { - d->m_mousePressed = false; auto currentDragState = d->m_dragState; d->m_globalDragStartMousePosition = QPoint(); d->m_dragStartMousePosition = QPoint(); diff --git a/src/libs/advanceddockingsystem/dockwidgettab.h b/src/libs/advanceddockingsystem/dockwidgettab.h index e6d025fec78..1b3c6f9d116 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.h +++ b/src/libs/advanceddockingsystem/dockwidgettab.h @@ -167,11 +167,6 @@ public: */ void setIconSize(const QSize &size); - /** - * Returns true, if the tab has been clicked and the mouse is currently pressed. - */ - bool mousePressed() const; - void setVisible(bool visible) override; signals: diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp index 9e547a20a5d..83f203c5e57 100644 --- a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp +++ b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp @@ -455,15 +455,19 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() if (!overlay->dropOverlayRect().isValid()) overlay = m_dockManager->dockAreaOverlay(); - // Resize the floating widget to the size of the highlighted drop area rectangle - QRect rect = overlay->dropOverlayRect(); - int frameWidth = (q->frameSize().width() - q->rect().width()) / 2; - int titleBarHeight = q->frameSize().height() - q->rect().height() - frameWidth; - if (rect.isValid()) { - QPoint topLeft = overlay->mapToGlobal(rect.topLeft()); - topLeft.ry() += titleBarHeight; - q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight))); - QApplication::processEvents(); + // Do not resize if we drop into an autohide sidebar area to preserve the dock area size + // for the initial size of the auto hide area. + if (!internal::isSideBarArea(overlay->dropAreaUnderCursor())) { + // Resize the floating widget to the size of the highlighted drop area rectangle + QRect rect = overlay->dropOverlayRect(); + int frameWidth = (q->frameSize().width() - q->rect().width()) / 2; + int titleBarHeight = q->frameSize().height() - q->rect().height() - frameWidth; + if (rect.isValid()) { + QPoint topLeft = overlay->mapToGlobal(rect.topLeft()); + topLeft.ry() += titleBarHeight; + q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight))); + QApplication::processEvents(); + } } m_dropContainer->dropFloatingWidget(q, QCursor::pos()); } @@ -510,10 +514,23 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &globalPositi } int visibleDockAreas = topContainer->visibleDockAreaCount(); - containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); + + DockWidgetAreas allowedContainerAreas = (visibleDockAreas > 1) ? OuterDockAreas : AllDockAreas; + auto dockArea = topContainer->dockAreaAt(globalPosition); + // If the dock container contains only one single DockArea, then we need to respect the allowed + // areas - only the center area is relevant here because all other allowed areas are from the + // container. + if (visibleDockAreas == 1 && dockArea) + allowedContainerAreas.setFlag(CenterDockWidgetArea, + dockArea->allowedAreas().testFlag(CenterDockWidgetArea)); + + if (m_dockContainer->features().testFlag(DockWidget::DockWidgetPinnable)) + allowedContainerAreas |= AutoHideDockAreas; + + containerOverlay->setAllowedAreas(allowedContainerAreas); + DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer); containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea); - auto dockArea = topContainer->dockAreaAt(globalPosition); if (dockArea && dockArea->isVisible() && visibleDockAreas > 0) { dockAreaOverlay->enableDropPreview(true); dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea diff --git a/src/libs/advanceddockingsystem/floatingdragpreview.cpp b/src/libs/advanceddockingsystem/floatingdragpreview.cpp index d9298870f99..6bbaf9ee22f 100644 --- a/src/libs/advanceddockingsystem/floatingdragpreview.cpp +++ b/src/libs/advanceddockingsystem/floatingdragpreview.cpp @@ -4,6 +4,7 @@ #include "floatingdragpreview.h" #include "ads_globals_p.h" +#include "ads_globals.h" #include "autohidedockcontainer.h" #include "dockareawidget.h" #include "dockcontainerwidget.h" @@ -31,6 +32,7 @@ class FloatingDragPreviewPrivate public: FloatingDragPreview *q; QWidget *m_content = nullptr; + DockWidget::DockWidgetFeatures m_contentFeatures; DockAreaWidget *m_contentSourceArea = nullptr; QPoint m_dragStartMousePosition; DockManager *m_dockManager = nullptr; @@ -70,19 +72,35 @@ public: void createFloatingWidget(); /** - * Returns true, if the content is floatable. + * Returns true, if the content is floatable */ bool isContentFloatable() const + { + return m_contentFeatures.testFlag(DockWidget::DockWidgetFloatable); + } + + /** + * Returns true, if the content is pinnable + */ + bool isContentPinnable() const + { + return m_contentFeatures.testFlag(DockWidget::DockWidgetPinnable); + } + + /** + * Returns the content features + */ + DockWidget::DockWidgetFeatures contentFeatures() const { DockWidget *dockWidget = qobject_cast(m_content); - if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) - return true; + if (dockWidget) + return dockWidget->features(); DockAreaWidget *dockArea = qobject_cast(m_content); - if (dockArea && dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) - return true; + if (dockArea) + return dockArea->features(); - return false; + return DockWidget::DockWidgetFeatures(); } }; // class FloatingDragPreviewPrivate @@ -107,8 +125,6 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition m_dropContainer = topContainer; auto containerOverlay = m_dockManager->containerOverlay(); auto dockAreaOverlay = m_dockManager->dockAreaOverlay(); - auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor(); - auto containerDropArea = containerOverlay->dropAreaUnderCursor(); if (!topContainer) { containerOverlay->hideOverlay(); @@ -119,6 +135,9 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition return; } + auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor(); + auto containerDropArea = containerOverlay->dropAreaUnderCursor(); + int visibleDockAreas = topContainer->visibleDockAreaCount(); // Include the overlay widget we're dragging as a visible widget @@ -126,8 +145,22 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition if (dockAreaWidget && dockAreaWidget->isAutoHide()) visibleDockAreas++; - containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); + DockWidgetAreas allowedContainerAreas = (visibleDockAreas > 1) ? OuterDockAreas : AllDockAreas; + auto dockArea = topContainer->dockAreaAt(globalPosition); + // If the dock container contains only one single DockArea, then we need + // to respect the allowed areas - only the center area is relevant here because + // all other allowed areas are from the container + if (visibleDockAreas == 1 && dockArea) + allowedContainerAreas.setFlag(CenterDockWidgetArea, + dockArea->allowedAreas().testFlag(CenterDockWidgetArea)); + + if (isContentPinnable()) + allowedContainerAreas |= AutoHideDockAreas; + + containerOverlay->setAllowedAreas(allowedContainerAreas); + containerOverlay->enableDropPreview(containerDropArea != InvalidDockWidgetArea); + if (dockArea && dockArea->isVisible() && visibleDockAreas >= 0 && dockArea != m_contentSourceArea) { dockAreaOverlay->enableDropPreview(true); @@ -148,12 +181,12 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition } else { dockAreaOverlay->hideOverlay(); // If there is only one single visible dock area in a container, then it does not make - // sense to show a dock overlay because the dock area would be removed and inserted at - // the same position. + // sense to show a dock overlay because the dock area would be removed and inserted at + // the same position. Only auto hide area is allowed. if (visibleDockAreas == 1) - containerOverlay->hideOverlay(); - else - containerOverlay->showOverlay(topContainer); + containerOverlay->setAllowedAreas(AutoHideDockAreas); + + containerOverlay->showOverlay(topContainer); if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea) m_dropContainer = nullptr; @@ -199,6 +232,7 @@ FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent) , d(new FloatingDragPreviewPrivate(this)) { d->m_content = content; + d->m_contentFeatures = d->contentFeatures(); setAttribute(Qt::WA_DeleteOnClose); if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); @@ -288,22 +322,25 @@ void FloatingDragPreview::finishDragging() // Non floatable auto hide widgets should stay in its current auto hide state if they are // dragged into a floating window. if (validDropArea || d->isContentFloatable()) - cleanupAutoHideContainerWidget(); + cleanupAutoHideContainerWidget(containerDropArea); if (!d->m_dropContainer) { d->createFloatingWidget(); } else if (dockDropArea != InvalidDockWidgetArea) { d->m_dropContainer->dropWidget(d->m_content, dockDropArea, - d->m_dropContainer->dockAreaAt(QCursor::pos())); + d->m_dropContainer->dockAreaAt(QCursor::pos()), + d->m_dockManager->dockAreaOverlay()->tabIndexUnderCursor()); } else if (containerDropArea != InvalidDockWidgetArea) { // If there is only one single dock area, and we drop into the center then we tabify the // dropped widget into the only visible dock area. if (d->m_dropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == containerDropArea) - d->m_dropContainer->dropWidget(d->m_content, - containerDropArea, - d->m_dropContainer->dockAreaAt(QCursor::pos())); + d->m_dropContainer + ->dropWidget(d->m_content, + containerDropArea, + d->m_dropContainer->dockAreaAt(QCursor::pos()), + d->m_dockManager->containerOverlay()->tabIndexUnderCursor()); else d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr); } else { @@ -315,15 +352,24 @@ void FloatingDragPreview::finishDragging() d->m_dockManager->dockAreaOverlay()->hideOverlay(); } -void FloatingDragPreview::cleanupAutoHideContainerWidget() +void FloatingDragPreview::cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea) { auto droppedDockWidget = qobject_cast(d->m_content); - if (droppedDockWidget && droppedDockWidget->autoHideDockContainer()) - droppedDockWidget->autoHideDockContainer()->cleanupAndDelete(); - auto droppedArea = qobject_cast(d->m_content); - if (droppedArea && droppedArea->autoHideDockContainer()) - droppedArea->autoHideDockContainer()->cleanupAndDelete(); + + auto autoHideContainer = droppedDockWidget ? droppedDockWidget->autoHideDockContainer() + : droppedArea->autoHideDockContainer(); + + if (!autoHideContainer) + return; + + // If the dropped widget is already an auto hide widget and if it is moved to a new side bar + // location in the same container, then we do not need to cleanup. + if (internal::isSideBarArea(containerDropArea) + && (d->m_dropContainer == autoHideContainer->dockContainer())) + return; + + autoHideContainer->cleanupAndDelete(); } void FloatingDragPreview::paintEvent(QPaintEvent *event) @@ -333,6 +379,7 @@ void FloatingDragPreview::paintEvent(QPaintEvent *event) return; QPainter painter(this); + painter.setOpacity(0.6); if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap); diff --git a/src/libs/advanceddockingsystem/floatingdragpreview.h b/src/libs/advanceddockingsystem/floatingdragpreview.h index 80bbb6dbec9..d3c5f85f7a5 100644 --- a/src/libs/advanceddockingsystem/floatingdragpreview.h +++ b/src/libs/advanceddockingsystem/floatingdragpreview.h @@ -89,7 +89,7 @@ public: // implements AbstractFloatingWidget /** * Cleanup auto hide container if the dragged widget has one. */ - void cleanupAutoHideContainerWidget(); + void cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea); signals: /**