3

I’m using Qt 4.8.3 on X11.

I need to know when the user ends with dragging a window around the screen, this in order to read the final position and eventually start an animation to adjust the window position to an “allowed” one.

I noticed that the QWidget::moveEvent is called for each small movement, but this is very unconvenient because I must perform position checking (and eventually start the animation) only when the user releases the mouse button and the movement is completely finished.

This is the real problem: it seems that there is no way to detect the mouse release event (or to get the mouse buttons status) when the user clicks on the titlebar, since it is controlled by the OS and not by Qt. I tried also with the QWidget::x11event(XEvent* e)… but the events are collected only inside the window, not the title bar, as well.

Does someone know a way to achieve this?

I suspect that I will have to reimplement the titlebar myself… too bad…

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Matteo Murgida
  • 161
  • 2
  • 7
  • You should probably also consider the situation where the user moves the window using the keyboard. (At least in Windows this is possible, I don't know about X11.) –  Oct 18 '12 at 14:55
  • QMoveEvent is posted every time widget is moved regardless to the way this moved was achieved. However, there is QResizeEvent, which potentially also affects animations in window. – divanov Oct 18 '12 at 17:02

3 Answers3

6

Realizing this is a very old question, it is the first hit that comes up when you try "Qt detecting the end of a window move event". So, I thought I'd add a solution that works well with the current (as of this writing) release of Qt, 5.12.3.

You can set up a small state machine that provides the boundaries for knowing when a top-level window's position has been altered using a QObject::eventFilter(). In Qt 5.12.x, you will receive a QEvent::NonClientAreaMouseButtonPress event when the mouse goes down in the non-client area of your window (e.g., the title bar), a subsequent QEvent::Move event when the window position changes (if at all), and then a final QEvent::NonClientAreaMouseButtonRelease event when the mouse button is released.

Knowing this sequence, and using a persistent Boolean state flag (user_moved_window) to know that the position actually changed, would give you the following code snippet within your QObject::eventFilter() method:

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    QEvent::Type event_type = event->type();
    [...]
    else if(event_type == QEvent::NonClientAreaMouseButtonPress)
        user_moved_window = false;
    else if(event_type == QEvent::Move && isVisible())
        user_moved_window = true;
    else if(event_type == QEvent::NonClientAreaMouseButtonRelease)
    {
        if(user_moved_window)
        {
            // do what you need to do to here to respond to
            // the end of the reposition event...

            user_moved_window = false;
        }
    }
    [...]
    return MainWindow::eventFilter(obj, event);
}

You might need to add some additional checks depending on your situation--e.g., to make sure the obj for the event is actually the main window--but this example works well for my production code using Qt 5.12.3.

b0bh00d
  • 121
  • 1
  • 5
5

I have the same problem as yours. The moveEvent is triggered at every point along its move and Qt provides no explicit method to figure out the end of the move.

But now, inspired by the answer of divanov, I found that when we release the mouse after moving the dialog, an event typed 173 would always be triggered. That's QEvent::NonClientAreaMouseMove.

So the code is straightforward.

First install the event filter and announce a member variable: int nLastEvent;.

bool Win::eventFilter(QObject *obj, QEvent *event)
{
    if (nLastEvent == QEvent::Move && event->type() == 173)
    {
        // do what you wanna do here when the mouse is released,
        // like attaching the dialog to the main window
    }
    nLastEvent = event->type();
    return QWidget::eventFilter(obj, event);
}

It's simple and efficient enough, isn't it!

Hope it be useful for you, too. :)

liulios
  • 51
  • 3
  • Your example won't work on my desktop, making the application not portable. As you can see from my log there is no QEvent::NonClientAreaMouseMove. What kind of distribution and window manger are you using? – divanov Nov 02 '12 at 15:44
  • I'm using Qt 4.8.3 and Win7. In your log the releasing is detected automatically. But that's not the situation Matteo Murgida encountered, though you share the same developing environment. However, my problem is similar and is solved. I'm just offering a different idea. :-) – liulios Nov 05 '12 at 05:30
  • If so, then my answer helped you to resolve your problem. Does it deserve an up-vote? – divanov Nov 08 '12 at 08:37
  • Yes, but my reputation's not enough..I just registered to share my exp. – liulios Nov 13 '12 at 01:51
2

Let's consider the following test application: main.cpp

#include <QApplication>

#include "win.h"

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

    Win w;
    w.show();

    return app.exec();
}

win.h:

#include <QWidget>
#include <QEvent>
#include <QMoveEvent>
#include <QDebug>

class Win : public QWidget
{
public:
    Win(QWidget *parent = 0) : QWidget(parent) {
        this->installEventFilter(this);
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event) {
        if (event->type() == QEvent::Move) {
            QMoveEvent *moveEvent = static_cast<QMoveEvent*>(event);
            qDebug() << "Move event:" << moveEvent->pos();
        } else {
            qDebug() << "Event type:" << event->type();
        }
        return QWidget::eventFilter(obj, event);
    }
};

This application just installs event filter on itself and prints to console all received events with special formatting for QMoveEvent to discriminate it in the log.

Typical log:

Event type: 203 
Event type: 75 
Move event: QPoint(0,0) 
Event type: 14 
Event type: 17 
Event type: 26 
Event type: 74 
Event type: 77 
Move event: QPoint(66,52) 
Event type: 12 
Event type: 24 
Event type: 99 
Event type: 77 
Event type: 12 
Event type: 10 
Event type: 11 
Move event: QPoint(308,356) 
Event type: 19 
Event type: 25 
Event type: 99 
Event type: 18 
Event type: 27 
Event type: 77 

As you see, there are 2 move events, when application was initially created and one, after I finished window movements. I was testing with Qt 4.8.1 and XOrg 7.6.

To check raw X events

  1. Have the test application running.
  2. Get window Id of the test application. To do so execute in command line xwininfo -name WINDOW_NAME, where WINDOW_NAME is the name of the test application's window. Another option is to use xwininfo without parameters, then you have to select the test application window with a mouse pointer.
  3. Run X event monitor xev -id 0x2a00002, where 0x2a00002 is window Id found in a previous step. This will print X events your window receives from X server. ConfigureNotify is X protocol counterpart of QMoveEvent.
divanov
  • 6,173
  • 3
  • 32
  • 51
  • 1
    Copying and pasting your code for me results in: `Move event: QPoint(683,135) Move event: QPoint(682,133) Move event: QPoint(680,131) Move event: QPoint(679,130) Move event: QPoint(678,128) Move event: QPoint(677,128) Move event: QPoint(676,128) Move event: QPoint(675,128) Move event: QPoint(674,128) Move event: QPoint(673,128) Move event: QPoint(672,128) Move event: QPoint(671,128) Move event: QPoint(670,128) ` Each delta-movement gets notified.... Qt4.8.3 XOrg 7.6 – Matteo Murgida Oct 19 '12 at 15:32
  • 1
    There are several options for what may be causing this. Could your verify X events coming to your application as to make sure it's not your Qt version issue. – divanov Oct 19 '12 at 16:01