Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Warning when closing an undocked qwidget



  • Hello,
    I've recently migrated code from qt 5.7 to qt 5.15 and when I close an undocked dock widget, I get this warning :

    QMainWindowLayout::tabPosition called with out-of-bounds value '0'

    If I close it programmaticaly by calling DockWidget->close(), there si no warning.
    If I dock and undock it before closing it, there is no warning either.

    What does this error mean?

    I have stepped in qt source, and when I close the widget by clicking on the X, the dockwidget close event call d->endDrag(true);
    But it does not call that function when I close it programmatically or when the widget is docked.


  • Lifetime Qt Champion

    Hi,

    Can you provide a minimal compilable example that shows that behaviour ?



  • The code is complex, and a single example doesnt show the issue. Thats why I hoped someone could explain what the error message means, so I would know where to search the issue.

    Gonna keep debugging on my side and update here if I fond something usefull


  • Lifetime Qt Champion

    Install Qt debug libs, set a breakpoint on the warning message and show us the backtrace.



  • Call stack is

    Qt5Widgetsd.dll!QMainWindowLayout::tabPosition(Qt::DockWidgetArea area) Line 1588 C++
    Qt5Widgetsd.dll!QDockWidgetPrivate::endDrag(bool abort) Line 870 C++
    Qt5Widgetsd.dll!QDockWidget::closeEvent(QCloseEvent * event) Line 1469 C++

    And in each fonction (check //This line comment)

    QTabWidget::TabPosition QMainWindowLayout::tabPosition(Qt::DockWidgetArea area) const
    {
        const auto dockPos = toDockPos(area);
        if (dockPos < QInternal::DockCount)
            return tabPositions[dockPos];
        qWarning("QMainWindowLayout::tabPosition called with out-of-bounds value '%d'", int(area)); //This line
        return QTabWidget::North;
    }
    
    void QDockWidgetPrivate::endDrag(bool abort)
    {
        Q_Q(QDockWidget);
        Q_ASSERT(state != nullptr);
    
        q->releaseMouse();
    
        if (state->dragging) {
            const QMainWindow *mainWindow = mainwindow_from_dock(q);
            Q_ASSERT(mainWindow != nullptr);
            QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
    
            if (abort || !mwLayout->plug(state->widgetItem)) {
                if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
                    // This QDockWidget will now stay in the floating state.
                    if (state->ownWidgetItem) {
                        delete state->widgetItem;
                        state->widgetItem = nullptr;
                    }
                    mwLayout->restore();
                    QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
                    if (!dwLayout->nativeWindowDeco()) {
                        // get rid of the X11BypassWindowManager window flag and activate the resizer
                        Qt::WindowFlags flags = q->windowFlags();
                        flags &= ~Qt::X11BypassWindowManagerHint;
                        q->setWindowFlags(flags);
                        setResizerActive(q->isFloating());
                        q->show();
                    } else {
                        setResizerActive(false);
                    }
                    if (q->isFloating()) { // Might not be floating when dragging a QDockWidgetGroupWindow
                        undockedGeometry = q->geometry();
    #if QT_CONFIG(tabwidget)
                        tabPosition = mwLayout->tabPosition(mainWindow->dockWidgetArea(q)); //This line
    #endif
                    }
                    q->activateWindow();
                } else {
                    // The tab was not plugged back in the QMainWindow but the QDockWidget cannot
                    // stay floating, revert to the previous state.
                    mwLayout->revert(state->widgetItem);
                }
            }
        }
        delete state;
        state = nullptr;
    }
    
    void QDockWidget::closeEvent(QCloseEvent *event)
    {
        Q_D(QDockWidget);
        if (d->state)
            d->endDrag(true); //This line
        QWidget::closeEvent(event);
    }
    

    And if I close the widget programatically, we dont go in d->enDrag(true) from closeEvent


  • Lifetime Qt Champion

    So you somehow get into QDockWidgetPrivate::initDrag(). Is it possible to set a breakpoint there to see why it is entered?



  • When closing the widget with the mouse (top right button)

    Call stack is

    Qt5Widgetsd.dll!QDockWidgetPrivate::initDrag(const QPoint & pos, bool nca) Line 778 C++
    Qt5Widgetsd.dll!QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent * event) Line 1043 C++
    Qt5Widgetsd.dll!QDockWidget::event(QEvent * event) Line 1586 C++
    Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3630 C++
    Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3580 C++
    Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1063 C++
    Qt5Cored.dll!QCoreApplication::forwardEvent(QObject * receiver, QEvent * event, QEvent * originatingEvent) Line 1079 C++
    Qt5Widgetsd.dll!QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent * e) Line 505 C++
    Qt5Widgetsd.dll!QWidgetWindow::event(QEvent * event) Line 308 C++
    Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3630 C++
    Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 2970 C++
    Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1063 C++
    Qt5Cored.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event) Line 1471 C++
    Qt5Guid.dll!QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent * e) Line 2278 C++
    Qt5Guid.dll!QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent * e) Line 2003 C++
    Qt5Guid.dll!QWindowSystemInterface::sendWindowSystemEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 1181 C++

    And for the last function:

    void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
    {
        Q_Q(QDockWidget);
    
        int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, q);
    
        QWidget *tl = q->topLevelWidget();
        QRect geo = tl->geometry();
        QRect titleRect = tl->frameGeometry();
        {
            titleRect.setLeft(geo.left());
            titleRect.setRight(geo.right());
            titleRect.setBottom(geo.top() - 1);
            titleRect.adjust(0, fw, 0, 0);
        }
    
        switch (event->type()) {
            case QEvent::NonClientAreaMouseButtonPress:
                if (!titleRect.contains(event->globalPos()))
                    break;
                if (state != nullptr)
                    break;
                if (qobject_cast<QMainWindow*>(parent) == 0 && qobject_cast<QDockWidgetGroupWindow*>(parent) == 0)
                    break;
                if (isAnimating())
                    break;
                initDrag(event->pos(), true); //This line
                if (state == nullptr)
                    break;
                state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
                                  (!hasFeature(this, QDockWidget::DockWidgetMovable) && q->isFloating());
                startDrag();
                break;
            case QEvent::NonClientAreaMouseMove:
                if (state == nullptr || !state->dragging)
                    break;
    
    #ifndef Q_OS_MAC
                if (state->nca) {
                    endDrag();
                }
    #endif
                break;
            case QEvent::NonClientAreaMouseButtonRelease:
    #ifdef Q_OS_MAC
                            if (state)
                                    endDrag();
    #endif
                            break;
            case QEvent::NonClientAreaMouseButtonDblClick:
                _q_toggleTopLevel();
                break;
            default:
                break;
        }
    }
    

  • Lifetime Qt Champion

    This is strange, I don't see anything within QDockWidget sources. But there were mouse handling changes in 5.12 - can you maybe check with 5.11.x and 5.12.x to see if this may be the culprit?



  • Just run basic tests with version 5.11.3 and 5.12.0.
    Looks like I dont have the issue with any of them


  • Lifetime Qt Champion

    Can you check latest 5.12.x?



  • I have managed to reproduce it with a really basic example:

    #include <QApplication>
    #include <QMainWindow>
    #include <QLabel>
    #include <QDockWidget>
    
    
    int main(int argc, char* argv[])
    {
        QApplication lApp(argc, argv);
    
        QMainWindow a;
        a.setCentralWidget(new QLabel("Main"));
        a.show();
    
        auto lWindow = new QDockWidget(&a);
        lWindow->setFloating(true);
        lWindow->show();
    
        return lApp.exec();
    }
    

    and the cmake file im using:

    cmake_minimum_required(VERSION 3.16.0)
    set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON" FORCE)
    
    project(test)
    
    #Qt libs and macro
    SET(CMAKE_PREFIX_PATH "C:/Qt/5.15.1/msvc2019_64")
    
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTORCC ON)
    set(CMAKE_AUTOUIC ON)
    
    find_package(Qt5 COMPONENTS Widgets Gui REQUIRED)
    
    set(Src
        ${CMAKE_CURRENT_LIST_DIR}/main.cpp
    )
    
    add_executable(Test ${Src})
    target_link_libraries(Test PRIVATE Qt5::Widgets Qt5::Gui)
    


  • Ran more test with this simple example, the issue is actually there with qt 5.11.3 ,5.12.0 and 5.12.9.

    Edit:
    I confirm the issue is not here with 5.7.1

    Should I test with more versions?
    Raise a bug?

    Do you know if there is a workaround?


  • Lifetime Qt Champion

    So you can also reproduce it with 5.11? Then it's not the change I thought.
    It would be nice when you find some time to investigate with which version the issue came in.


  • Lifetime Qt Champion

    This post is deleted!


  • The issue is there in 5.9.

    Cant test between 5.7.1 and 5.9, versions are not available for download


  • Lifetime Qt Champion

    Thx for the information. I would suggest you to create a bug reports with all your findings. Sadly I can't compile such an old version on my system anymore so I can't hep figuring out which exact commit creates this issue.



  • Bug report created: https://bugreports.qt.io/browse/QTBUG-88157
    (Cant figure out how to properly format the code in the report)


  • Lifetime Qt Champion

    Thx, I reformatted the code, the CMakeLists.txt is not needed.


Log in to reply