summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qdockwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets/qdockwidget.cpp')
-rw-r--r--src/widgets/widgets/qdockwidget.cpp205
1 files changed, 140 insertions, 65 deletions
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp
index 6072740db8..706306000c 100644
--- a/src/widgets/widgets/qdockwidget.cpp
+++ b/src/widgets/widgets/qdockwidget.cpp
@@ -119,7 +119,7 @@ bool QDockWidgetTitleButton::event(QEvent *event)
{
switch (event->type()) {
case QEvent::StyleChange:
- case QEvent::ScreenChangeInternal:
+ case QEvent::DevicePixelRatioChange:
m_iconSize = -1;
break;
default:
@@ -128,33 +128,12 @@ bool QDockWidgetTitleButton::event(QEvent *event)
return QAbstractButton::event(event);
}
-static inline bool isWindowsStyle(const QStyle *style)
-{
- // Note: QStyleSheetStyle inherits QWindowsStyle
- const QStyle *effectiveStyle = style;
-
-#if QT_CONFIG(style_stylesheet)
- if (style->inherits("QStyleSheetStyle"))
- effectiveStyle = static_cast<const QStyleSheetStyle *>(style)->baseStyle();
-#endif
-#if !defined(QT_NO_STYLE_PROXY)
- if (style->inherits("QProxyStyle"))
- effectiveStyle = static_cast<const QProxyStyle *>(style)->baseStyle();
-#endif
-
- return effectiveStyle->inherits("QWindowsStyle");
-}
-
QSize QDockWidgetTitleButton::dockButtonIconSize() const
{
if (m_iconSize < 0) {
m_iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
- // Dock Widget title buttons on Windows where historically limited to size 10
- // (from small icon size 16) since only a 10x10 XPM was provided.
- // Adding larger pixmaps to the icons thus caused the icons to grow; limit
- // this to qpiScaled(10) here.
- if (isWindowsStyle(style()))
- m_iconSize = qMin((10 * logicalDpiX()) / 96, m_iconSize);
+ if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, nullptr, this))
+ m_iconSize = (m_iconSize * 5) / 8; // 16 -> 10
}
return QSize(m_iconSize, m_iconSize);
}
@@ -253,7 +232,9 @@ bool QDockWidgetLayout::wmSupportsNativeWindowDeco()
return false;
#else
static const bool xcb = !QGuiApplication::platformName().compare("xcb"_L1, Qt::CaseInsensitive);
- return !xcb;
+ static const bool wayland =
+ QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive);
+ return !(xcb || wayland);
#endif
}
@@ -277,7 +258,7 @@ void QDockWidgetLayout::addItem(QLayoutItem*)
QLayoutItem *QDockWidgetLayout::itemAt(int index) const
{
int cnt = 0;
- for (int i = 0; i < item_list.count(); ++i) {
+ for (int i = 0; i < item_list.size(); ++i) {
QLayoutItem *item = item_list.at(i);
if (item == nullptr)
continue;
@@ -290,7 +271,7 @@ QLayoutItem *QDockWidgetLayout::itemAt(int index) const
QLayoutItem *QDockWidgetLayout::takeAt(int index)
{
int j = 0;
- for (int i = 0; i < item_list.count(); ++i) {
+ for (int i = 0; i < item_list.size(); ++i) {
QLayoutItem *item = item_list.at(i);
if (item == nullptr)
continue;
@@ -307,7 +288,7 @@ QLayoutItem *QDockWidgetLayout::takeAt(int index)
int QDockWidgetLayout::count() const
{
int result = 0;
- for (int i = 0; i < item_list.count(); ++i) {
+ for (int i = 0; i < item_list.size(); ++i) {
if (item_list.at(i))
++result;
}
@@ -627,12 +608,13 @@ void QDockWidgetPrivate::init()
QAbstractButton *button = new QDockWidgetTitleButton(q);
button->setObjectName("qt_dockwidget_floatbutton"_L1);
- QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
+ QObjectPrivate::connect(button, &QAbstractButton::clicked,
+ this, &QDockWidgetPrivate::toggleTopLevel);
layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
button = new QDockWidgetTitleButton(q);
button->setObjectName("qt_dockwidget_closebutton"_L1);
- QObject::connect(button, SIGNAL(clicked()), q, SLOT(close()));
+ QObject::connect(button, &QAbstractButton::clicked, q, &QDockWidget::close);
layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
font = QApplication::font("QDockWidgetTitle");
@@ -643,8 +625,8 @@ void QDockWidgetPrivate::init()
toggleViewAction->setMenuRole(QAction::NoRole);
fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
toggleViewAction->setText(fixedWindowTitle);
- QObject::connect(toggleViewAction, SIGNAL(triggered(bool)),
- q, SLOT(_q_toggleView(bool)));
+ QObjectPrivate::connect(toggleViewAction, &QAction::triggered,
+ this, &QDockWidgetPrivate::toggleView);
#endif
updateButtons();
@@ -679,7 +661,7 @@ void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
option->verticalTitleBar = l->verticalTitleBar;
}
-void QDockWidgetPrivate::_q_toggleView(bool b)
+void QDockWidgetPrivate::toggleView(bool b)
{
Q_Q(QDockWidget);
if (b == q->isHidden()) {
@@ -727,7 +709,7 @@ void QDockWidgetPrivate::updateButtons()
layout->invalidate();
}
-void QDockWidgetPrivate::_q_toggleTopLevel()
+void QDockWidgetPrivate::toggleTopLevel()
{
Q_Q(QDockWidget);
q->setFloating(!q->isFloating());
@@ -754,7 +736,7 @@ void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
state = new QDockWidgetPrivate::DragState;
state->pressPos = pos;
state->globalPressPos = q->mapToGlobal(pos);
- state->widgetInitialPos = q->pos();
+ state->widgetInitialPos = q->isFloating() ? q->pos() : q->mapToGlobal(QPoint(0, 0));
state->dragging = false;
state->widgetItem = nullptr;
state->ownWidgetItem = false;
@@ -768,7 +750,7 @@ void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
tabbed widgets, and false if the dock widget should always be dragged
alone.
*/
-void QDockWidgetPrivate::startDrag(bool group)
+void QDockWidgetPrivate::startDrag(DragScope scope)
{
Q_Q(QDockWidget);
@@ -778,7 +760,11 @@ void QDockWidgetPrivate::startDrag(bool group)
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != nullptr);
- state->widgetItem = layout->unplug(q, group);
+#if QT_CONFIG(draganddrop)
+ bool wasFloating = q->isFloating();
+#endif
+
+ state->widgetItem = layout->unplug(q, scope);
if (state->widgetItem == nullptr) {
/* Dock widget has a QMainWindow parent, but was never inserted with
QMainWindow::addDockWidget, so the QMainWindowLayout has no
@@ -796,6 +782,20 @@ void QDockWidgetPrivate::startDrag(bool group)
layout->restore();
state->dragging = true;
+
+#if QT_CONFIG(draganddrop)
+ if (QMainWindowLayout::needsPlatformDrag()) {
+ Qt::DropAction result =
+ layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos);
+ if (result == Qt::IgnoreAction && !wasFloating) {
+ layout->revert(state->widgetItem);
+ delete state;
+ state = nullptr;
+ } else {
+ endDrag(QDockWidgetPrivate::EndDragMode::LocationChange);
+ }
+ }
+#endif
}
/*! \internal
@@ -803,7 +803,7 @@ void QDockWidgetPrivate::startDrag(bool group)
The \a abort parameter specifies that it ends because of programmatic state
reset rather than mouse release event.
*/
-void QDockWidgetPrivate::endDrag(bool abort)
+void QDockWidgetPrivate::endDrag(EndDragMode mode)
{
Q_Q(QDockWidget);
Q_ASSERT(state != nullptr);
@@ -815,7 +815,11 @@ void QDockWidgetPrivate::endDrag(bool abort)
Q_ASSERT(mainWindow != nullptr);
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
- if (abort || !mwLayout->plug(state->widgetItem)) {
+ // if mainWindow is being deleted in an ongoing drag, make it a no-op instead of crashing
+ if (!mwLayout)
+ return;
+
+ if (mode == EndDragMode::Abort || !mwLayout->plug(state->widgetItem)) {
if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
// This QDockWidget will now stay in the floating state.
if (state->ownWidgetItem) {
@@ -847,6 +851,11 @@ void QDockWidgetPrivate::endDrag(bool abort)
tabPosition = mwLayout->tabPosition(toDockWidgetArea(dwgw->layoutInfo()->dockPos));
}
#endif
+ // Reparent, if the drag was out of a dock widget group window
+ if (mode == EndDragMode::LocationChange) {
+ if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(q->parentWidget()))
+ groupWindow->reparent(q);
+ }
}
q->activateWindow();
} else {
@@ -875,6 +884,10 @@ Qt::DockWidgetArea QDockWidgetPrivate::toDockWidgetArea(QInternal::DockPosition
void QDockWidgetPrivate::setResizerActive(bool active)
{
Q_Q(QDockWidget);
+ const auto *dwLayout = qobject_cast<QDockWidgetLayout *>(layout);
+ if (dwLayout->nativeWindowDeco(q->isFloating()))
+ return;
+
if (active && !resizer)
resizer = new QWidgetResizeHandler(q);
if (resizer)
@@ -937,13 +950,22 @@ bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton && titleArea.contains(event->position().toPoint()) &&
hasFeature(this, QDockWidget::DockWidgetFloatable)) {
- _q_toggleTopLevel();
+ toggleTopLevel();
return true;
}
}
return false;
}
+bool QDockWidgetPrivate::isTabbed() const
+{
+ Q_Q(const QDockWidget);
+ QDockWidget *that = const_cast<QDockWidget *>(q);
+ auto *mwLayout = qt_mainwindow_layout_from_dock(that);
+ Q_ASSERT(mwLayout);
+ return mwLayout->isDockWidgetTabbed(q);
+}
+
bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
{
bool ret = false;
@@ -974,7 +996,8 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
} else
#endif
{
- startDrag();
+ const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
+ startDrag(scope);
q->grabMouse();
ret = true;
}
@@ -1006,19 +1029,25 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
- windowMarginOffset;
} else {
// Fallback in the unlikely case that source and target screens could not be established
- qCWarning(lcQpaDockWidgets)
+ qCDebug(lcQpaDockWidgets)
<< "QDockWidget failed to find relevant screen info. screenFrom:" << screenFrom
<< "screenTo:" << screenTo << " wdgScreen:" << wdgScreen << "orgWdgScreen"
<< orgWdgScreen;
pos = event->globalPosition().toPoint() - state->pressPos - windowMarginOffset;
}
+ // If the newly floating dock widget has got a native title bar,
+ // offset the position by the native title bar's height or width
+ const int dx = q->geometry().x() - q->x();
+ const int dy = q->geometry().y() - q->y();
+ pos.rx() += dx;
+ pos.ry() += dy;
+
QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
if (floatingTab && !q->isFloating())
floatingTab->move(pos);
else
q->move(pos);
-
if (state && !state->ctrlDrag)
mwlayout->hover(state->widgetItem, event->globalPosition().toPoint());
@@ -1032,9 +1061,15 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
{
#if QT_CONFIG(mainwindow)
+#if QT_CONFIG(draganddrop)
+ // if we are peforming a platform drag ignore the release here and end the drag when the actual
+ // drag ends.
+ if (QMainWindowLayout::needsPlatformDrag())
+ return false;
+#endif
if (event->button() == Qt::LeftButton && state && !state->nca) {
- endDrag();
+ endDrag(EndDragMode::LocationChange);
return true; //filter out the event
}
@@ -1073,26 +1108,25 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
break;
state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
(!hasFeature(this, QDockWidget::DockWidgetMovable) && q->isFloating());
- startDrag();
+ startDrag(DragScope::Group);
break;
case QEvent::NonClientAreaMouseMove:
if (state == nullptr || !state->dragging)
break;
#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
- if (state->nca) {
- endDrag();
- }
+ if (state->nca)
+ endDrag(EndDragMode::LocationChange);
#endif
break;
case QEvent::NonClientAreaMouseButtonRelease:
#if defined(Q_OS_MAC) || defined(Q_OS_WASM)
if (state)
- endDrag();
+ endDrag(EndDragMode::LocationChange);
#endif
break;
case QEvent::NonClientAreaMouseButtonDblClick:
- _q_toggleTopLevel();
+ toggleTopLevel();
break;
default:
break;
@@ -1158,10 +1192,10 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
return; // this dockwidget can't be redocked
}
- bool wasFloating = q->isFloating();
+ const bool wasFloating = q->isFloating();
if (wasFloating) // Prevent repetitive unplugging from nested invocations (QTBUG-42818)
unplug = false;
- bool hidden = q->isHidden();
+ const bool hidden = q->isHidden();
if (q->isVisible())
q->hide();
@@ -1179,8 +1213,12 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
flags |= Qt::FramelessWindowHint;
}
- if (unplug)
+#if QT_CONFIG(draganddrop)
+ // If we are performing a platform drag the flag is not needed and we want to avoid recreating
+ // the platform window when it would be removed later
+ if (unplug && !QMainWindowLayout::needsPlatformDrag())
flags |= Qt::X11BypassWindowManagerHint;
+#endif
q->setWindowFlags(flags);
@@ -1256,15 +1294,13 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
possible to drag the dock widget when undocking. Starting the drag will undock
the dock widget, but a second drag will be needed to move the dock widget itself.
- \sa QMainWindow, {Dock Widgets Example}
+ \sa QMainWindow
*/
/*!
\enum QDockWidget::DockWidgetFeature
- \value DockWidgetClosable The dock widget can be closed. On some systems the dock
- widget always has a close button when it's floating
- (for example on MacOS 10.5).
+ \value DockWidgetClosable The dock widget can be closed.
\value DockWidgetMovable The dock widget can be moved between docks
by the user.
\value DockWidgetFloatable The dock widget can be detached from the
@@ -1396,9 +1432,12 @@ QDockWidget::DockWidgetFeatures QDockWidget::features() const
\property QDockWidget::floating
\brief whether the dock widget is floating
- A floating dock widget is presented to the user as an independent
- window "on top" of its parent QMainWindow, instead of being
- docked in the QMainWindow.
+ A floating dock widget is presented to the user as a single, independent
+ window "on top" of its parent QMainWindow, instead of being docked
+ either in the QMainWindow, or in a group of tabbed dock widgets.
+
+ Floating dock widgets can be individually positioned and resized, both
+ programmatically or by mouse interaction.
By default, this property is \c true.
@@ -1412,7 +1451,7 @@ void QDockWidget::setFloating(bool floating)
// the initial click of a double-click may have started a drag...
if (d->state != nullptr)
- d->endDrag(true);
+ d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
QRect r = d->undockedGeometry;
// Keep position when undocking for the first time.
@@ -1467,8 +1506,14 @@ void QDockWidget::changeEvent(QEvent *event)
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
switch (event->type()) {
- case QEvent::ModifiedChange:
case QEvent::WindowTitleChange:
+ if (isFloating() && windowHandle() && d->topData() && windowHandle()->isVisible()) {
+ // From QWidget::setWindowTitle(): Propagate window title without signal emission
+ d->topData()->caption = windowHandle()->title();
+ d->setWindowTitle_helper(windowHandle()->title());
+ }
+ Q_FALLTHROUGH();
+ case QEvent::ModifiedChange:
update(layout->titleArea());
#ifndef QT_NO_ACTION
d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
@@ -1494,8 +1539,14 @@ void QDockWidget::closeEvent(QCloseEvent *event)
{
Q_D(QDockWidget);
if (d->state)
- d->endDrag(true);
- QWidget::closeEvent(event);
+ d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
+
+ // For non-closable widgets, don't allow closing, except when the mainwindow
+ // is hidden, as otherwise an application wouldn't be able to be shut down.
+ const QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
+ const bool canClose = (d->features & DockWidgetClosable)
+ || (!win || !win->isVisible());
+ event->setAccepted(canClose);
}
/*! \reimp */
@@ -1571,7 +1622,7 @@ bool QDockWidget::event(QEvent *event)
bool onTop = false;
if (win != nullptr) {
const QObjectList &siblings = win->children();
- onTop = siblings.count() > 0 && siblings.last() == (QObject*)this;
+ onTop = siblings.size() > 0 && siblings.last() == (QObject*)this;
}
#if QT_CONFIG(tabbar)
if (!isFloating() && layout != nullptr && onTop)
@@ -1685,6 +1736,10 @@ QAction * QDockWidget::toggleViewAction() const
invisible). This happens when the widget is hidden or shown, as
well as when it is docked in a tabbed dock area and its tab
becomes selected or unselected.
+
+ \note The signal can differ from QWidget::isVisible(). This can be the case, if
+ a dock widget is minimized or tabified and associated to a non-selected or
+ inactive tab.
*/
/*!
@@ -1766,6 +1821,26 @@ QWidget *QDockWidget::titleBarWidget() const
return layout->widgetForRole(QDockWidgetLayout::TitleBar);
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+
+ if (!dockWidget) {
+ dbg << "QDockWidget(0x0)";
+ return dbg;
+ }
+
+ dbg << "QDockWidget(" << static_cast<const void *>(dockWidget);
+ dbg << "->(ObjectName=" << dockWidget->objectName();
+ dbg << "; floating=" << dockWidget->isFloating();
+ dbg << "; features=" << dockWidget->features();
+ dbg << ";))";
+ return dbg;
+}
+#endif // QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
#include "qdockwidget.moc"