ADS: Integrate newest base repository commits

* Add drag and drop to auto hide

Base repository was merged until commit
65600a4dcd072fd2773b661823816db6392c34eb

Change-Id: I09dd6613869368d3cf0c701055a6972db915561d
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Henning Gruendl
2023-07-19 17:03:29 +02:00
committed by Henning Gründl
parent 16ef838f23
commit bb635325e7
26 changed files with 1224 additions and 353 deletions

View File

@@ -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());

View File

@@ -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.

View File

@@ -90,6 +90,7 @@ struct AutoHideDockContainerPrivate
ResizeHandle *m_resizeHandle = nullptr;
QSize m_size; // creates invalid size
QPointer<AutoHideTab> 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
*/

View File

@@ -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

View File

@@ -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,19 +178,28 @@ 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)
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);
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<AutoHideTab *>(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();

View File

@@ -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.

View File

@@ -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 <QApplication>
#include <QBoxLayout>
#include <QContextMenuEvent>
#include <QElapsedTimer>
#include <QMenu>
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<typename T>
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();
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();
switch (m_sideBar->sideBarLocation()) {
case SideBarLeft:
startPos.rx() = autoHideContainer->rect().left() + 10;
break;
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));
AutoHideSideBar *AutoHideTab::sideBar() const
{
return d->m_sideBar;
}
void AutoHideTab::removeFromSideBar()
{
if (d->m_sideBar == nullptr)
return;
d->m_sideBar->removeTab(this);
setSideBar(nullptr);
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

View File

@@ -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

View File

@@ -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<DockWidgetTab *>(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<DockWidgetTab *>(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

View File

@@ -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
*/

View File

@@ -44,10 +44,10 @@ class DockAreaTitleBarPrivate
{
public:
DockAreaTitleBar *q;
QPointer<TitleBarButtonType> m_tabsMenuButton;
QPointer<TitleBarButtonType> m_autoHideButton;
QPointer<TitleBarButtonType> m_undockButton;
QPointer<TitleBarButtonType> m_closeButton;
QPointer<TitleBarButton> m_tabsMenuButton;
QPointer<TitleBarButton> m_autoHideButton;
QPointer<TitleBarButton> m_undockButton;
QPointer<TitleBarButton> 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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.

View File

@@ -84,7 +84,7 @@ public:
QList<AutoHideDockContainer *> m_autoHideWidgets;
QMap<SideBarLocation, AutoHideSideBar *> 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<DockAreaWidget *>(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<DockAreaWidget *>(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<DockWidget *>(widget);
auto droppedArea = qobject_cast<DockAreaWidget *>(widget);
if (droppedDockWidget) {
DockAreaWidget *oldDockArea = droppedDockWidget->dockAreaWidget();
if (oldDockArea == targetArea)
return;
if (oldDockArea)
oldDockArea->removeDockWidget(droppedDockWidget);
targetArea->insertDockWidget(0, droppedDockWidget, true);
} else {
QList<DockWidget *> 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<DockAreaWidget *>(widget);
if (area)
return area->isCentralWidgetArea();
auto innerSplitter = qobject_cast<DockSplitter *>(widget);
if (innerSplitter)
return innerSplitter->isResizingWithContainer();
return false;
}
void DockContainerWidgetPrivate::moveToContainer(QWidget *widget, DockWidgetArea area)
{
DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(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<DockWidget *>(widget);
auto droppedArea = qobject_cast<DockAreaWidget *>(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<DockWidget *> 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<DockWidget *>(widget);
DockAreaWidget *droppedDockArea = qobject_cast<DockAreaWidget *>(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<DockAreaWidget *>(widget);
if (area)
return area->isCentralWidgetArea();
auto innerSplitter = qobject_cast<DockSplitter *>(widget);
if (innerSplitter)
return innerSplitter->isResizingWithContainer();
return false;
}
void DockContainerWidgetPrivate::addDockAreasToList(const QList<DockAreaWidget *> 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<QSplitter *>(widget);
auto *childSplitter = qobject_cast<DockSplitter *>(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<QSplitter *>(splitter);
auto *parentSplitter = internal::findParent<QSplitter *>(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);
// 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<QSplitter *>(newRootSplitter);
d->m_rootSplitter = qobject_cast<DockSplitter *>(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

View File

@@ -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

View File

@@ -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)

View File

@@ -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 <utils/hostosinfo.h>
@@ -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<DockContainerWidget *>(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<DockContainerWidget *>(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<DockAreaWidget *>(d->m_targetWidget.data());
if (!dockArea)
auto cursorPos = QCursor::pos();
auto dockArea = qobject_cast<DockAreaWidget *>(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<DockContainerWidget *>(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();

View File

@@ -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

View File

@@ -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,6 +751,9 @@ void DockWidget::setFloating()
if (isClosed())
return;
if (isAutoHide())
dockAreaWidget()->setFloating();
else
d->m_tabWidget->detachDockWidget();
}
@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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:

View File

@@ -455,6 +455,9 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
if (!overlay->dropOverlayRect().isValid())
overlay = m_dockManager->dockAreaOverlay();
// 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;
@@ -465,6 +468,7 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
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

View File

@@ -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<DockWidget *>(m_content);
if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable))
return true;
if (dockWidget)
return dockWidget->features();
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(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);
@@ -149,10 +182,10 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition
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.
// the same position. Only auto hide area is allowed.
if (visibleDockAreas == 1)
containerOverlay->hideOverlay();
else
containerOverlay->setAllowedAreas(AutoHideDockAreas);
containerOverlay->showOverlay(topContainer);
if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea)
@@ -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,
d->m_dropContainer
->dropWidget(d->m_content,
containerDropArea,
d->m_dropContainer->dockAreaAt(QCursor::pos()));
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<DockWidget *>(d->m_content);
if (droppedDockWidget && droppedDockWidget->autoHideDockContainer())
droppedDockWidget->autoHideDockContainer()->cleanupAndDelete();
auto droppedArea = qobject_cast<DockAreaWidget *>(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);

View File

@@ -89,7 +89,7 @@ public: // implements AbstractFloatingWidget
/**
* Cleanup auto hide container if the dragged widget has one.
*/
void cleanupAutoHideContainerWidget();
void cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea);
signals:
/**