Create a QDockWidget that resizes to it's contents

前端 未结 1 1967
我在风中等你
我在风中等你 2021-01-13 11:08

I have an application where fixed-size child widgets need to be added programatically to a dock widget at run time based on user input. I want to add these widgets to a dock

1条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-13 12:03

    The problem is, nothing in the code above actually causes the QMainWindowLayout to recalculate itself. That function is buried within the QMainWindowLayout private class, but can be stimulated by adding and removing a dummy QDockWidget, which causes the layout to invalidate and recalcualte the dock widget positions

    QDockWidget* dummy = new QDockWidget;
    mainWindow->addDockWidget(Qt::TopDockWidgetArea, dummy);
    mainWindow->removeDockWidget(dummy);
    

    The only problem with this is that if you dig into the QT source code, you'll see that adding a dock widget causes the dock separator to be released, which causes unintuitive and choppy behavior as the user tries to resize the dock, and the mouse unexpectedly 'lets go'.

    void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
                                                 QDockWidget *dockwidget,
                                                 Qt::Orientation orientation)
    {
        addChildWidget(dockwidget);
    
        // If we are currently moving a separator, then we need to abort the move, since each
        // time we move the mouse layoutState is replaced by savedState modified by the move.
        if (!movingSeparator.isEmpty())
            endSeparatorMove(movingSeparatorPos);
    
        layoutState.dockAreaLayout.addDockWidget(toDockPos(area), dockwidget, orientation);
        emit dockwidget->dockLocationChanged(area);
        invalidate();
    }
    

    That can be corrected by moving the cursor back onto the separator and simulating a mouse press, basically undoing the endSeparatorMove callafter the docks have been repositioned. It's important to post the event, rather than send it, so thatit occurs after the resize event. The code for doing so looks like:

    QPoint mousePos = mainWindow->mapFromGlobal(QCursor::pos());
    mousePos.setY(dock->rect().bottom()+2);
    QCursor::setPos(mainWindow->mapToGlobal(mousePos));
    QMouseEvent* grabSeparatorEvent = 
        new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
    qApp->postEvent(mainWindow, grabSeparatorEvent);
    

    Where 2 is a magic number that accounts for the group box border.

    Put that all together, and here is the event filter than gives the desired behavior:

    Corrected Event Filter

    #ifndef QDockResizeEventFilter_h__
    #define QDockResizeEventFilter_h__
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "QFluidGridLayout.h"
    
    class QDockResizeEventFilter : public QObject
    {
    
    public:
        friend QMainWindow;
        friend QLayoutPrivate;
        QDockResizeEventFilter(QWidget* dockChild, QFluidGridLayout* layout, QObject* parent = 0)
            : QObject(parent), m_dockChild(dockChild), m_layout(layout)
        {
    
        }
    
    protected:
    
        bool eventFilter(QObject *p_obj, QEvent *p_event)
        {  
            if (p_event->type() == QEvent::Resize)
            {
                QResizeEvent* resizeEvent   = static_cast(p_event);
                QMainWindow* mainWindow     = dynamic_cast(p_obj->parent());              
                QDockWidget* dock           = static_cast(p_obj);
    
                // determine resize direction
                if (resizeEvent->oldSize().height() != resizeEvent->size().height())
                {
                    // vertical expansion
                    QSize fixedSize(m_layout->widthForHeight(m_dockChild->size().height()), m_dockChild->size().height());
                    if (dock->size().width() != fixedSize.width())
                    {
    
                        m_dockChild->setFixedWidth(fixedSize.width());
                        dock->setFixedWidth(fixedSize.width());
    
                        // cause mainWindow dock layout recalculation
                        QDockWidget* dummy = new QDockWidget;
                        mainWindow->addDockWidget(Qt::TopDockWidgetArea, dummy);
                        mainWindow->removeDockWidget(dummy);
    
                        // adding dock widgets causes the separator move event to end
                        // restart it by synthesizing a mouse press event
                        QPoint mousePos = mainWindow->mapFromGlobal(QCursor::pos());
                        mousePos.setY(dock->rect().bottom()+2);
                        QCursor::setPos(mainWindow->mapToGlobal(mousePos));
                        QMouseEvent* grabSeparatorEvent = new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
                        qApp->postEvent(mainWindow, grabSeparatorEvent);
                    }
                }
                if (resizeEvent->oldSize().width() != resizeEvent->size().width())
                {
                    // horizontal expansion
                    // ...
                }           
            }   
            return false;
        }
    
    private:
    
        QWidget* m_dockChild;
        QFluidGridLayout* m_layout;
    };
    
    #endif // QDockResizeEventFilter_h__
    

    0 讨论(0)
提交回复
热议问题