Jepessen
Jepessen

Reputation: 12415

Move a window by clicking an internal widget instead of title bar

In Windows when I create a QMainWindow I can move it around the screen by clicking the title bar and dragging it.

In my application I've hidden the title bar by using setWindowFlags(Qt::CustomizeWindowHint) and I'm trying to build a custom title bar using a widget and setting it in the menu space with setMenuWidget(myWidget).

Now I want to reproduce the original behaviour: I want to click on my MyWidget widget inside the QMainWindow and, while mouse is pressed, dragging the mouse moves the window.

Is there a way to do it?

Upvotes: 0

Views: 3126

Answers (2)

Mike
Mike

Reputation: 8355

This is an example on how to implement a fake title bar, that has standard buttons (minimize, maximize, close), and can be dragged to move the whole window (this is based on the approach in @Kevin's answer).

screenshot

#include <QtWidgets>


class FakeTitleBar : public QWidget{
    Q_OBJECT
public:
    explicit FakeTitleBar(QWidget* parent= nullptr):QWidget(parent){
        label.setSizePolicy(QSizePolicy::Expanding,
                            QSizePolicy::Expanding);
        layout.addWidget(&label);
        layout.addWidget(&buttonMinimize);
        layout.addWidget(&buttonMaximize);
        layout.addWidget(&buttonClose);
        //connecting buttons' signals to slots
        connect(&buttonMinimize, &QPushButton::clicked,
                this, &FakeTitleBar::MinimizeWindow);
        connect(&buttonMaximize, &QPushButton::clicked,
                this, &FakeTitleBar::MaximizeWindow);
        connect(&buttonClose, &QPushButton::clicked,
                this, &FakeTitleBar::CloseWindow);
        //setting vertical fixed size policy
        //so that the title bar does not take up any additional space
        setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
        //a bit of styling
        setStyleSheet("QPushButton {margin:0px; padding:5px;}"
                      "QWidget {background-color:blue; color:white;}");
    }

public slots:
    //slots for corresponding buttons
    void MinimizeWindow(){
        window()->showMinimized();
    }
    void MaximizeWindow(){
        if(!window()->isMaximized())
            window()->showMaximized();
        else
            window()->showNormal();
    }
    void CloseWindow(){
        window()->close();
    }

protected:
    void mousePressEvent(QMouseEvent* event){
        //save the press position (this is relative to the current widget)
        pressPos= event->pos();
        isMoving= true;
    }
    void mouseMoveEvent(QMouseEvent* event){
        //isMoving flag makes sure that the drag and drop event originated
        //from within the titlebar, because otherwise the window shouldn't be moved
        if(isMoving){
            //calculate difference between the press position and the new Mouse position
            //(this is relative to the current widget)
            QPoint diff= event->pos() - pressPos;
            //move the window by diff
            window()->move(window()->pos()+diff);
        }
    }
    void mouseReleaseEvent(QMouseEvent* /*event*/){
        //drag and drop operation end
        isMoving= false;
    }
    //double-clicking on the title bar should maximize the window
    void mouseDoubleClickEvent(QMouseEvent* /*event*/){
        MaximizeWindow();
    }
    //in order for the style sheet to apply on this custom widget
    //see https://doc.qt.io/qt-5/stylesheet-reference.html#qwidget-widget
    void paintEvent(QPaintEvent *)
    {
        QStyleOption opt;
        opt.init(this);
        QPainter p(this);
        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    }

private:
    QHBoxLayout layout{this};
    QLabel label{"Fake Title Bar"};
    QPushButton buttonMinimize{"-"};
    QPushButton buttonMaximize{"M"};
    QPushButton buttonClose{"X"};
    QPoint pressPos;
    bool isMoving{false};
};

//sample usage

class Widget : public QWidget{
public:
    explicit Widget(QWidget* parent= nullptr):QWidget(parent){
        setWindowFlags(Qt::CustomizeWindowHint);
        layout.addWidget(&titleBar);
        layout.addWidget(&label);
        layout.setContentsMargins(0, 0, 0, 0);
        label.setAlignment(Qt::AlignCenter);
        //default size for the window
        resize(320,240);
    }
    ~Widget(){}

private:
    QVBoxLayout layout{this};
    FakeTitleBar titleBar;
    QLabel label{"this is a sample window"};
};

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);

    Widget w;
    w.show();

    return app.exec();
}

#include "main.moc"

Upvotes: 7

Kevin Krammer
Kevin Krammer

Reputation: 5207

You just need to implement the necessary mouse event handling by overwriting MyWidget's mousePressEvent(), mouseMoveEvent() and mouseReleaseEvent() handlers.

  1. Detect the mouse down, get current mouse position
  2. While moving, get current mouse position, calculate difference, save new position, move window by diff

You can get the window (top level widget) from inside MyWidget through the window() method.

Upvotes: 2

Related Questions