Moving QDockWindow to another QMainWindow during drag.



  • Hello all,

    The title says it all : I want to change QMainWindow of a QDockWindow while I'm dragging the said QDockWindow.

    While I'm not dragging I can do this just by calling addDockWidget on the new QMainWindow. If I do the same while I'm dragging the QDockWindow a crash occures.

    I can get around it by doing a setFloating( true ) before the addDockWidget but that stops the dragging and moves the QDockWidget to the specified dock area (obviously). I want the user to be albe to continue dragging seamlessly.

    Any ideas ?

    Ian


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Can you provide a minimal sample code that can reproduce the problem ?



  • I made a subclass of QDockWidget and wrote an override for the event method. This QDockWidget can be in one of two main windows, m_pMainWindow1 is the normal main window and m_pMainWindow2 is another main window used as a the central widget of m_pMainWindow1-.

    @bool MultiWindowDockWidget::event ( QEvent* in_pEvent )
    {
    switch ( in_pEvent->type() )
    {
    case QEvent::MouseButtonPress:
    case QEvent::NonClientAreaMouseButtonPress:
    {
    QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>( in_pEvent );
    if( pMouseEvent->button() == Qt::LeftButton && !m_bIsDragging )
    m_bIsDragging = true;
    }
    break;
    case QEvent::Move:
    {
    if( m_bIsDragging )
    {
    if( m_pMainWindow2->rect().contains( m_pMainWindow2->mapFromGlobal( QCursor::pos() ) ) )
    {
    if( !m_pMainWindow2->children().contains( this ) )--
    {
    m_bNeedWindowChange = true;
    update();
    }
    }
    else
    {
    if( !m_pMainWindow1--->children().contains( this ) )
    {
    m_bNeedWindowChange = true;
    update();
    }
    }
    }
    }
    break;
    case QEvent::MouseButtonRelease:
    case QEvent::NonClientAreaMouseButtonRelease:
    case QEvent::NonClientAreaMouseMove:
    {
    if( m_bIsDragging )
    m_bIsDragging = false;
    }
    break;
    case QEvent::UpdateRequest:
    {
    if( m_bNeedWindowChange )
    {
    QRect oGeometry = geometry();
    setFloating( false );
    if( m_pMainWindow2->children().contains( this ) )
    {
    m_pMainWindow1->addDockWidget( Qt::LeftDockWidgetArea, this );
    }
    else
    {
    m_pMainWindow2->addDockWidget( Qt::LeftDockWidgetArea, this );
    }
    setFloating( true );
    m_bNeedWindowChange = false;--
    }
    }
    break;
    }
    return QDockWidget::event( in_pEvent );
    }@

    This is the best work around that I've found but every time the dock widget changes main window the dragging stops, so it's not an acceptable solution for the user.

    I'm also not sure if the update() method and UpdateRequest event are made to be used like this? At first, I tried to put that code directly in the Move event case but this caused a crash because the Move event is triggered by a MouseMove event. The MouseMove event crashes because it tries to do stuff with the drag state of the QDockWidget (that becomes invalid because dragging stops) after the Move event returns.


  • Lifetime Qt Champion

    What does your crash say ?



  • The code I posted before doesn't crash. The crash occurs when I try to change the main window during the move event.

    It's a memory violation exception when trying to acces the state attribute of the QDockWidget. From what I can gather, state holds infomation about the dragging of the QDockWidget and it is set to null when dragging isn't in progress.

    @
    if (state->dragging && !state->nca) {
    QPoint pos = event->globalPos() - state->pressPos;
    q->move(pos);

        if (!state->ctrlDrag)
            mwlayout->hover(state->widgetItem, event->globalPos());
    
        ret = true;
    }
    

    @

    During the call to q->move(pos) I intercept the move event and change the main window, which also stops the dragging and sets state to null:

    @
    bool MultiWindowDockWidget::event ( QEvent* in_pEvent )
    {
    switch ( in_pEvent->type() )
    {
    case QEvent::MouseButtonPress:
    case QEvent::NonClientAreaMouseButtonPress:
    {
    QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>( in_pEvent );
    if( pMouseEvent->button() == Qt::LeftButton && !m_bIsDragging )
    m_bIsDragging = true;
    }
    break;
    case QEvent::Move:
    {
    if( m_bIsDragging )
    {
    if( m_pMainWindow2->rect().contains( m_pMainWindow2->mapFromGlobal( QCursor::pos() ) ) )
    {
    if( !m_pMainWindow2->children().contains( this ) )
    {
    m_pMainWindow2->addDockWidget( Qt::LeftDockWidgetArea, this );
    }
    }
    else
    {
    if( !m_pMainWindow1--->children().contains( this ) )
    {
    m_pMainWindow1->addDockWidget( Qt::LeftDockWidgetArea, this );
    }
    }
    }
    }
    break;
    case QEvent::MouseButtonRelease:
    case QEvent::NonClientAreaMouseButtonRelease:
    case QEvent::NonClientAreaMouseMove:
    {
    if( m_bIsDragging )
    m_bIsDragging = false;
    }
    break;
    }
    return QDockWidget::event( in_pEvent );
    }
    @

    Once q->move(pos) returns, the QDockWidget tries to acces the state attribute if (!state->ctrlDrag) and crashes.

    Idealy I would like to change the main window during the move event and then reactivate dragging (this would stop the crash and be seamless to the user) but I don't have acces to the initDrag(), startDrag(), and endDrag() methods in QDockWidget that would enable me to do this.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.