Skip to content

Commit

Permalink
MAYA-131961: Apply docking fixes from Qt 6.7
Browse files Browse the repository at this point in the history
This patch includes a series of 14 commits that were cherry-picked from
Qt dev branch at the time Qt 6.7 was being developed. The commits were
squashed together into one commit to more clearly delineate them as
going together.

These changes are intended to be included in Maya 2025.1

Here is my working list of things I cherry-picked, with titles and a few notes.

---
Starting from the tip of dev, collecting all related commits that should be
cherry-picked into Maya's Qt 6.5.3 branch after Senthil's QDockWidget patch is
reverted:

3591ea9  - Not strictly necessary, but improves internal docs for QDockWidgetGroupWindow regarding need for it to not be focusable when only one child QDockWidget

Sorta related, but not specifically to this relation chain:
d462c7b - QMainWindowTabBar: make destructor public

e6d85cf -- This is the main fix we want - "QDockWidget: Fix group unplugging"
0b10b74 - Refactor tst_QDockWidget::closeAndDelete()
26ded9d - tst_QDockWidget: use local context for timer
17372fa - Remove bool trap in QDockWidgetPrivate::endDrag()  -- NOTE: This one requires manual conflict resolution in QDockWidget::startDrag, drop that change. The change it makes is in a newer commit from Qt 6.6 related to Wayland that we don't really need for this fix.
c93ab8c - QDockWidget: Remove "group" bool trap -- this one also requires manual conflict resolution, but I can't find out what is different...
c02f8b9 - Fully export QDockWidget debug operator
bbeff2a - Extend qDebug functionality for QDockWidget

b6b489d - QMainWindowTabBar: Add destructor
2c96f51 - QDockAreaLayout: implement widget based add() and remove()
1ab91b7 - QDockWidget: call raise() when a dock widget starts to hover
4c60a11 - QDockWidget: Always show dock widgets with the main window
0e435b7 - QMainWindowLayout: remove redundant #ifdef'ry
---

Below you will find the commit messages of each of the cherry-picked
commits, starting with the newest (so, for cherry-picking, you would
apply in reverse order).

At the top of the entry will have the qt dev branch commit hash that was
cherry-picked, for easy lookup.

Below that will be an indication that manual conflict resolution was
needed to apply it to this branch, and what manual changes were needed.

=================================
cherry-picked from 3591ea9

Harden internal documentation of QDockWidgetGroupWindow

QDockWidgetGroupWindow was documented by a few lines of code comments,
omitting the most critical part of its design: It must not become able
to acquire focus (i.e. be dragged or dropped) while having only one
QDockWidget child.

=> replace legacy code comments by structured internal documentation.

Pick-to: 6.6 6.5
Change-Id: I410ebf2e4c20c7479038417a4d8448dce8ab995f
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from d462c7b

QMainWindowTabBar: make destructor public

Was private by mistake => make it public.

Pick-to: 6.6 6.5
Change-Id: I6b07a19687ddf84e8456aa70bc34b1cc714a299e
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from e6d85cf

QDockWidget: Fix group unplugging

A floating dock widget could either be a single QDockWidget object,
or a QDockWidgetGroupWindow with a single QDockWidget child.
The former could be dropped on the latter. Dropping the latter on
the former caused a crash.

The existence of QDockWidgetGroupWindows with a single dock widget
child was accepted to be by design.
Previous fixes, such as  9ff40b5,
attempted to wrap all single floating dock widgets in
QDockWidgetGroupWindows.

These attempts fell short, because of the manifold programmatic and
manual options to create a floating dock widget:
- drag a single dock widget out of a main window dock area
- drag a dock widget out of a tab bar on the main window
- drag a dock widget out of a floating tab
- call `QDockWidget::setFloating(true)` in any situation
- create a new QDockWidget, that floats from the beginning

Whenever a QDockWidgetGroupWindow with a single QDockWidget child
was hovered and/or dropped on a QDockWidget without a group window,
crashes or screen artifacts were observed. Previous fixes made them
occur less often.

QDockWidgetGroupWindow is not designed to hold a single QDockWidget
child. Such a state is inconsistent and may only exist, while a
QDockWidgetGroupWindow is constructed.

The reason why such invalid QDockWidgetGroupWindows started to exist,
is a bool trap: QDockWidgetPrivate::mouseMoveEvent() starts a drag
operation, when a dock widget is moved by mouse.
It called startDrag() with no argument, which defaulted to
startDrag(true) and caused a group drag.

This assumption is
*correct*, when a tabbed group of dock widgets is dragged out of the
main dock as a whole, to become floating tabs.
*wrong*, when a single dock widget is dragged out of a docked group,
to become a single floating dock widget.

In the second case, the dock widget was wrapped in a new, floating,
invisible QDockWidgetGroupWindow. Looking like a single, floating dock
widget, the group window caused a crash, when attempted to be dropped
on another dock widget.

This patch eliminates all cases, where a QDockWidgetGroupWindow with
a single QDockWidget is created:
(1) Implement QDockWidgetPrivate::isTabbed().
This enables mouseMoveEvent to determine, whether the move relates to a
group of tabbed dock widgets, or to a single dock widget.
startDrag() can therefore be called with the right argument. It will no
longer create a QDockWidgetGroupWindow with a single QDockWidget child.

(2) Change QMainWindowTabBar::mouseReleaseEvent
When a dock widget was dragged out of a tab bar and became a single,
floating dock widget, it was still parented to the group window.
That is wrong, because it has no more relationship with that group
window.
=> Reparent it to the main window, just like any other single floating
dock widget. That enables QDockWidgetGroupWindow to detect, that the
2nd last child has gone and no more group window is needed (see next
point).

(3) React to reparenting, closing and deleting
If the second last dock widget in a floating tab gets closed (manually
or programmatically), reparented or deleted, also unplug the last one
and remove the group window.

(4) Amend 9ff40b5
Remove the code path where a QDockWidgetGroupWindow with a single
QDockWidget child was created 'just in case', to make it compatible
others, created by (1), (2) or (3).

(5) Change QMainWindowLayout::hover()
When the hover ends without a successful drop and a temporary group
window with a single dock widget child has been created, remove the
group window.

The patch fixes smaller inconsistencies, which have not become visible
due to assertions and crashes earlier in the chain.

The patch finally extends tst_QDockWidget, to cover all 4 cases.

- Creation of floating tabs
The creation of floating tabs is extracted from floatingTabs() to
the helper function createFloatingTabs(). In addition to creating
floating tabs, the helper verifies that dragging a dock widget out
of the main window doesn't accidently wrap it in a group window.
This covers case (1).

- tst_QDockWidget::floatingTabs()
The test function verifies now, that both test dock widgets have the
same path before plugging them together and after unplugging them from
the floating tab. This covers case(4).

- tst_QDockwidget::deleteFloatingTabWithSingleDockWidget()
This test function is added, to cover cases (2) and (3).

- tst_QDockWidget::hoverWithoutDrop()
This test function hovers two floating dock widgets hover each other,
and returns the moved dock widget to its origin before releasing the
mouse. This covers case(5).

This fixes a lot of long standing bugs, making the author of this patch
modestly happy :-)

Fixes: QTBUG-118223
Fixes: QTBUG-99136
Fixes: QTBUG-118578
Fixes: QTBUG-118579
Fixes: QTBUG-56799
Fixes: QTBUG-35736
Fixes: QTBUG-63448
Fixes: QTBUG-88329
Fixes: QTBUG-88157
Fixes: QTBUG-94097
Fixes: QTBUG-44540
Fixes: QTBUG-53808
Fixes: QTBUG-72915
Fixes: QTBUG-53438
Found-by: Keith Kyzivat <[email protected]>
Found-by: Frederic Lefebvre <[email protected]>
Pick-to: 6.6 6.5
Change-Id: I51b5f9e40cb2dbe55fb14d769541067730538463
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from 0b10b74

Refactor tst_QDockWidget::closeAndDelete()

The test function was temporarily blacklisted on Ubuntu, but also
failing on other Linux platforms (e.g. openSuSE).

It tested, whether closing all dock widgets and the main window, would
close the application as well. It used one single shot timer, to close
the windows and later one to check, whether the application was shut
down.

While that mechanism must work in an application environment, it is not
guaranteed to work in testlib. More specifically, I could happen that
the XCB / glib event loop continued to spin and wait for events.

=> Check the signal QGuiApplication::lastWindowClosed() instead. If the
signal is fired, it is proven that all windows have been closed and
the application would quit in a production environment.

The underlying test case was: Application didn't quit with the last
dock widget closed, because there was a dangling
QDockWidgetGroupWindow.

=> finally: Clean up BLACKLIST

Pick-to: 6.6 6.5
Change-Id: Ic5fde5967fc8dde70ab64dc30cc7367c908b5c51
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from 26ded9d

tst_QDockWidget: use local context for timer

If we use 'this' as context it might still try to invoke
the timer after we have left the function, leading to
stack-use-after-return.

To avoid doing an in-depth dive if it's okay to use mainWindow as the
context or not, I just added a new local QObject.

Amends 9ff40b5

Pick-to: 6.6 6.5
Change-Id: I2c3bdc1eb06731d9c38979610303876c2748fb73
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from 17372fa

Manual conflict resolution: This was built ontop of a change made for wayland,
which I decided didn't need to be cherry-picked. In the conflict area, just drop
the one conflict as the change made was modifying the change introduced by the
wayland change.

Remove bool trap in QDockWidgetPrivate::endDrag()

endDrag(false) meant to end a drag with a dock location change.
endDrag(true) meant to abort a drag without a dock location change.

Replace this with a meaningful enumeration.
Define a dummy enum for builds w/o QDockWidget.

Task-number: QTBUG-118578
Task-number: QTBUG-118579
Pick-to: 6.6 6.5
Change-Id: I786f4210f5a3ee67ffcf0dc9285f77a480148569
Reviewed-by: David Faure <[email protected]>

=================================
cherry-picked from c93ab8c

Manual conflict resolution: Unclear what changed - all changes in the commit
apply, but git stopped for manual conflict resolution. Possibly a whitespace
change.

QDockWidget: Remove "group" bool trap

The unplug() and startDrag() functions of QMainWindowLayout and
QDockWidget used a boolean argument specifying whether a single dock
widget or a group of dock widgets should be unplugged. The argument
defaulted to true.

That has lead to inconsistent unplug operations, broken item_lists and
crashes, especially when the methods were called without an argument.

To improve code readability, replace bool trap with a meaningful
enum. Remove default arguments, in order to force explicit calls.

This patch does not change behavior, it is just carved out to
facilitate reviews.

Task-number: QTBUG-118578
Task-number: QTBUG-118579
Pick-to: 6.6 6.5
Change-Id: I50341b055f0bb76c2797b2fb1126a10de1fee7dd
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from c02f8b9

Fully export QDockWidget debug operator

Amends bbeff2a.

Task-number: QTBUG-118578
Task-number: QTBUG-118579
Found-by: Friedemann Kleint <[email protected]>
Pick-to: 6.6
Change-Id: I60d8d11a82ff5de8b9641c86b824969fc9a34b91
Reviewed-by: Friedemann Kleint <[email protected]>
Reviewed-by: Fabian Kosmale <[email protected]>

=================================
cherry-picked from bbeff2a

Extend qDebug functionality for QDockWidget

Add features and floating flag to QDockWidget debugging.
Debug dockwidget parents of a group window.

Task-number: QTBUG-118578
Task-number: QTBUG-118579
Pick-to: 6.6
Change-Id: If2a6caacf5d02f9018c2a3073fdbc1de39bce1ee
Reviewed-by: Santhosh Kumar <[email protected]>
Reviewed-by: Keith Kyzivat <[email protected]>

=================================
cherry-picked from b6b489d

QMainWindowTabBar: Add destructor

QMainWindowLayout re-uses tab bars. A QSet and a QList member are kept,
to track used and unused tab bars.

Corner cases upon application close down leave dangling pointers in
those containers.

=> Add a destructor to QMainWindowTabBar
=> remove the tab bar from used and unused tab bar containers, if
not directly parented to the main window.

=> No longer reparent unused tab bars of a QDockWidgetGroupWindow
to the main window. Let them be destroyed as a group window child,
and its destructor remove it from the used/unused tab bar container.

Pick-to: 6.6 6.5
Change-Id: If2388cf878553dc89583dbc8585748fad65bbab2
Reviewed-by: Volker Hilsheimer <[email protected]>

=================================
cherry-picked from 2c96f51

QDockAreaLayout: implement widget based add() and remove()

The item_list of a QDockAreaLayoutInfo has abstraction methods for
reading the item list. Adding to and removing from the item list is
done directly, by using the QList api.

Implement an abstraction, that takes a QWidget *.
The argument may either be a QDockWidgetGroupWindow or a QDockWidget.

Task-number: QTBUG-118578
Task-number: QTBUG-118579
Pick-to: 6.6 6.5
Change-Id: Ib2ccd7557a21a43b68f184fe4575018f2a97004b
Reviewed-by: David Faure <[email protected]>

=================================
cherry-picked from 1ab91b7

QDockWidget: call raise() when a dock widget starts to hover

When dock widget (1) starts to hover over another floating dock
widget (2), the latter animates a rubber band, to indicate to the user
that it is ready to accept a drop.

The creation of a QRubberBand moves (2) one position up in the Z order.
The consequence is a visual glitch: While
- the mouse cursor dragging (1) is still outside (2) and
- the visual rectangle of (1) starts overlapping (2)
(1) hides behind (2).
As soon as the mouse cursor enters (2), (1) suddenly comes on top and
(2) hides behind (1).

=> raise() 1 as soon as it starts hovering. That brings it on top of
the Z order, which is expected behavior.

Pick-to: 6.6 6.5
Change-Id: I1140fc6ff109c7a713e7e2617072698467375585
Reviewed-by: Richard Moe Gustavsen <[email protected]>
Reviewed-by: David Faure <[email protected]>

=================================
cherry-picked from 4c60a11

QDockWidget: Always show dock widgets with the main window

QMainWindow::show() also showed its dock widget children. When a main
window with dock widget children consumed a show event for another
reason, hidden dock widget children remained hidden.

If a dock widget application went to the background, e.g. because it
was hidden behind another application gaining focus, a klick on the
dock widget application's app icon would not show its dock widget
children. Unless the dock widget application provides shows them
explicitly, they can never been shown again by the user.

=> show all dock widget and group window children, when QMainWindow
consumes a show event.

Pick-to: 6.6 6.5
Change-Id: I7e8b59f021ec4ec5679d0d08d0eeda1e3225a385
Reviewed-by: David Faure <[email protected]>

=================================
cherry-picked from 0e435b7

QMainWindowLayout: remove redundant #ifdef'ry

Remove #if QT_CONFIG(dockwidget) nested within each other.

Pick-to: 6.6 6.5
Change-Id: I6c662909676ffada3ac52e41a9c2d8b9fd491689
Reviewed-by: David Faure <[email protected]>
  • Loading branch information
keithel-qt committed Jan 6, 2024
1 parent 7e11a55 commit db413ca
Show file tree
Hide file tree
Showing 11 changed files with 616 additions and 204 deletions.
47 changes: 44 additions & 3 deletions src/widgets/widgets/qdockarealayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,27 @@ QDockAreaLayoutItem
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem *item)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
return item ? dbg << *item : dbg << "QDockAreaLayoutItem(0x0)";
}

QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem &item)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QDockAreaLayoutItem(" << static_cast<const void *>(&item) << "->";
if (item.widgetItem) {
dbg << "widgetItem(" << item.widgetItem->widget() << ")";
QWidget *widget = item.widgetItem->widget();
if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
dbg << "widgetItem(" << dockWidget << ")";
} else if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
dbg << "widgetItem(" << groupWindow << "->(" << groupWindow->dockWidgets() << "))";
} else {
dbg << "widgetItem(" << widget << ")";
}
} else if (item.subinfo) {
dbg << "subInfo(" << item.subinfo << "->(" << item.subinfo->item_list << ")";
} else if (item.placeHolderItem) {
Expand Down Expand Up @@ -1004,6 +1018,14 @@ void QDockAreaLayoutInfo::remove(const QList<int> &path)
}
}

void QDockAreaLayoutInfo::remove(QWidget *widget)
{
const QList<int> path = indexOf(widget);
if (path.isEmpty())
return;
remove(path);
}

QLayoutItem *QDockAreaLayoutInfo::plug(const QList<int> &path)
{
Q_ASSERT(!path.isEmpty());
Expand Down Expand Up @@ -1134,8 +1156,6 @@ bool QDockAreaLayoutInfo::insertGap(const QList<int> &path, QLayoutItem *dockWid
index = -index - 1;
}

// dump(qDebug() << "insertGap() before:" << index << tabIndex, *this, QString());

if (path.size() > 1) {
QDockAreaLayoutItem &item = item_list[index];

Expand Down Expand Up @@ -1764,6 +1784,26 @@ QLayoutItem *QDockAreaLayoutInfo::takeAt(int *x, int index)
return nullptr;
}

// Add a dock widget or dock widget group window to the item list
void QDockAreaLayoutInfo::add(QWidget *widget)
{
// Do not add twice
if (!indexOf(widget).isEmpty())
return;

if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(dockWidget)));
return;
}

if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
item_list.append(QDockAreaLayoutItem(new QDockWidgetGroupWindowItem(groupWindow)));
return;
}

qFatal("Coding error. Add supports only QDockWidget and QDockWidgetGroupWindow");
}

void QDockAreaLayoutInfo::deleteAllLayoutItems()
{
for (int i = 0; i < item_list.size(); ++i) {
Expand Down Expand Up @@ -1957,6 +1997,7 @@ bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*>
if (testing) {
//was it is not really added to the layout, we need to delete the object here
delete item.widgetItem;
item.widgetItem = nullptr;
}
}
} else if (nextMarker == SequenceMarker) {
Expand Down
3 changes: 3 additions & 0 deletions src/widgets/widgets/qdockarealayout_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct Q_AUTOTEST_EXPORT QDockAreaLayoutItem
uint flags;
#ifndef QT_NO_DEBUG_STREAM
friend Q_AUTOTEST_EXPORT QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem &item);
friend Q_AUTOTEST_EXPORT QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem *item);
#endif
};

Expand Down Expand Up @@ -108,6 +109,7 @@ class Q_AUTOTEST_EXPORT QDockAreaLayoutInfo
QList<int> gapIndex(const QPoint &pos, bool nestingEnabled,
TabMode tabMode) const;
void remove(const QList<int> &path);
void remove(QWidget *widget);
void unnest(int index);
void split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem);
#if QT_CONFIG(tabbar)
Expand Down Expand Up @@ -155,6 +157,7 @@ class Q_AUTOTEST_EXPORT QDockAreaLayoutInfo

QLayoutItem *itemAt(int *x, int index) const;
QLayoutItem *takeAt(int *x, int index);
void add(QWidget *widget);
void deleteAllLayoutItems();

QMainWindowLayout *mainWindowLayout() const;
Expand Down
61 changes: 48 additions & 13 deletions src/widgets/widgets/qdockwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,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);

Expand All @@ -778,7 +778,7 @@ void QDockWidgetPrivate::startDrag(bool group)
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != nullptr);

state->widgetItem = layout->unplug(q, group);
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
Expand All @@ -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);
Expand All @@ -815,7 +815,7 @@ void QDockWidgetPrivate::endDrag(bool abort)
Q_ASSERT(mainWindow != nullptr);
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);

if (abort || !mwLayout->plug(state->widgetItem)) {
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) {
Expand Down Expand Up @@ -847,6 +847,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 {
Expand Down Expand Up @@ -944,6 +949,15 @@ bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
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;
Expand Down Expand Up @@ -974,7 +988,8 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
} else
#endif
{
startDrag();
const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
startDrag(scope);
q->grabMouse();
ret = true;
}
Expand Down Expand Up @@ -1040,7 +1055,7 @@ bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
#if QT_CONFIG(mainwindow)

if (event->button() == Qt::LeftButton && state && !state->nca) {
endDrag();
endDrag(EndDragMode::LocationChange);
return true; //filter out the event
}

Expand Down Expand Up @@ -1079,22 +1094,21 @@ 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:
Expand Down Expand Up @@ -1418,7 +1432,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.
Expand Down Expand Up @@ -1506,7 +1520,7 @@ void QDockWidget::closeEvent(QCloseEvent *event)
{
Q_D(QDockWidget);
if (d->state)
d->endDrag(true);
d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
QWidget::closeEvent(event);
}

Expand Down Expand Up @@ -1782,6 +1796,27 @@ 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();
return dockWidget ? dbg << *dockWidget : dbg << "QDockWidget(0x0)";
}

QDebug operator<<(QDebug dbg, const QDockWidget &dockWidget)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
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"
Expand Down
5 changes: 5 additions & 0 deletions src/widgets/widgets/qdockwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ class Q_WIDGETS_EXPORT QDockWidget : public QWidget
inline bool isAreaAllowed(Qt::DockWidgetArea area) const
{ return (allowedAreas() & area) == area; }

#ifndef QT_NO_DEBUG_STREAM
friend Q_WIDGETS_EXPORT QDebug operator<<(QDebug dbg, const QDockWidget &dockWidget);
friend Q_WIDGETS_EXPORT QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget);
#endif

#ifndef QT_NO_ACTION
QAction *toggleViewAction() const;
#endif
Expand Down
15 changes: 13 additions & 2 deletions src/widgets/widgets/qdockwidget_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ class QDockWidgetPrivate : public QWidgetPrivate
};

public:
enum class DragScope {
Group,
Widget
};

enum class EndDragMode {
LocationChange,
Abort
};

void init();
void _q_toggleView(bool); // private slot
void _q_toggleTopLevel(); // private slot
Expand Down Expand Up @@ -86,8 +96,8 @@ class QDockWidgetPrivate : public QWidgetPrivate
void setWindowState(bool floating, bool unplug = false, const QRect &rect = QRect());
void nonClientAreaMouseEvent(QMouseEvent *event);
void initDrag(const QPoint &pos, bool nca);
void startDrag(bool group = true);
void endDrag(bool abort = false);
void startDrag(DragScope scope);
void endDrag(EndDragMode mode);
void moveEvent(QMoveEvent *event);
void recalculatePressPos(QResizeEvent *event);

Expand All @@ -96,6 +106,7 @@ class QDockWidgetPrivate : public QWidgetPrivate
void setResizerActive(bool active);

bool isAnimating() const;
bool isTabbed() const;

private:
QWidgetResizeHandler *resizer = nullptr;
Expand Down
7 changes: 7 additions & 0 deletions src/widgets/widgets/qmainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,13 @@ bool QMainWindow::event(QEvent *event)
return true;
#endif // QT_CONFIG(statustip)

#if QT_CONFIG(dockwidget)
case QEvent::Show:
Q_ASSERT(d->layout);
d->layout->showDockWidgets();
break;
#endif // QT_CONFIG(dockwidget)

case QEvent::StyleChange:
#if QT_CONFIG(dockwidget)
Q_ASSERT(d->layout);
Expand Down
Loading

0 comments on commit db413ca

Please sign in to comment.