macosqtresizewidgetborderless

Qt resize frameless widget on MacOSX


I need to create a frameless widget in Qt, and it needs to be resizable. The app will run under Windows and Mac OSX.

If I use: setWindowFlags(Qt::FramelessWindowHint); I can resize the window from the bottom-right corner (a QSizeGrip appears, I guess it's included in QMainWindow widget).

I can add grips at each corner, but I want the window to be resizable from the sides (and not only the corners). Is there any easy way to make it resizable from all sides like a normal window?

There is a workaround for Windows, which consists on override nativeEvent handler: Qt/Windows, resizable frameless window , but I couldn't find a solution for Mac OSX platform.


Solution

  • The best way to do this is to catch the mouse move events and show the cursor appropriate for where you are and do the resizing when the button is held down. There is an example that provides a Frame widget which you can put your own content one into.

        #include <QtWidgets>
    
        class Frame : public QFrame
        {
        public:
    
        Frame()
        {
        m_mouse_down = false;
        setFrameShape(Panel);
    
        // Make this a borderless window which can't
        // be resized or moved via the window system
        setWindowFlags(Qt::FramelessWindowHint);
        setMouseTracking(true);
    
        m_content = new QWidget(this);
    
        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(m_content);
        layout->setMargin(5);
        layout->setSpacing(0);
        setLayout(layout);
        }
    
        // Allows you to access the content area of the frame
        // where widgets and layouts can be added
        QWidget *contentWidget() const { return m_content; }
    
        void mousePressEvent(QMouseEvent *e)
        {
        m_old_pos = e->pos();
        m_mouse_down = e->button() == Qt::LeftButton;
        }
    
        void mouseMoveEvent(QMouseEvent *e)
        {
        int x = e->x();
        int y = e->y();
    
        if (m_mouse_down) {
        int dx = x - m_old_pos.x();
        int dy = y - m_old_pos.y();
    
        QRect g = geometry();
    
        if (left)
        g.setLeft(g.left() + dx);
        if (right)
        g.setRight(g.right() + dx);
        if (bottom)
        g.setBottom(g.bottom() + dy);
        if (top)
        g.setTop(g.top() + dy);
    
        setGeometry(g);
    
        m_old_pos = QPoint(!left ? e->x() : m_old_pos.x(), e->y());
        } else {
        QRect r = rect();
        top = qAbs(y - r.top()) <= 5;
        left = qAbs(x - r.left()) <= 5;
        right = qAbs(x - r.right()) <= 5;
        bottom = qAbs(y - r.bottom()) <= 5;
        bool hor = left | right;
    
        if (hor && bottom) {
        if (left)
        setCursor(Qt::SizeBDiagCursor);
        else
        setCursor(Qt::SizeFDiagCursor);
        } else if (hor) {
        setCursor(Qt::SizeHorCursor);
        } else if (bottom || top) {
        setCursor(Qt::SizeVerCursor);
        } else {
        setCursor(Qt::ArrowCursor);
        }
        }
        }
    
        void mouseReleaseEvent(QMouseEvent *e)
        {
        m_mouse_down = false;
        }
    
        private:
        QWidget *m_content;
        QPoint m_old_pos;
        bool m_mouse_down;
        bool left, right, bottom, top;
        };
    
    
        #include "main.moc"
    
        int main(int argc, char **argv)
        {
        QApplication app(argc, argv);
    
        Frame box;
    
        QVBoxLayout *l = new QVBoxLayout(box.contentWidget());
        l->setMargin(0);
        QTextEdit *edit = new QTextEdit(box.contentWidget());
        l->addWidget(edit);
    
        box.show();
        return app.exec();
        }