1

I inherited QQuickWindow and created a frame-less window that can be moved by drag. Inside my window I put a Slider element. The problem is that the Slider forwards the events to the parent window and when I try to change the value on the slider, the window moves along. Here's how it behaves:

enter image description here

Is there a possibility to make the slider accept the mouse events and not forward them to the window?

Here's my code:

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QUrl>

#include "mywindow.h"

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

    qmlRegisterType<MyWindow>("mycustomlib", 1, 0, "MyWindow");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import mycustomlib 1.0

MyWindow {
    width: 300
    height: 180
    visible: true
    x: 250
    y: 250
    color: "beige"

    Slider {
        anchors.fill: parent
        value: 0.5
    }
}

mywindow.h

#ifndef MYWINDOW_H
#define MYWINDOW_H

#include <QQuickWindow>

class MyWindow : public QQuickWindow
{
    Q_OBJECT

public:
    MyWindow(QWindow *pParent = Q_NULLPTR);

protected:
    virtual void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
    virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
    virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE;

private:
    bool m_move;
    QPoint m_initialMouseClickPos;
};

#endif // MYWINDOW_H

mywindow.cpp

#include "mywindow.h"

#include <QDebug>
#include <QCursor>

MyWindow::MyWindow(QWindow *pParent) :
    QQuickWindow(pParent),
    m_move(false)
{
    setFlags(Qt::FramelessWindowHint);
}

void MyWindow::mouseMoveEvent(QMouseEvent *e)
{
    if (m_move) {
        const QPoint newMousePosition = e->pos() - m_initialMouseClickPos + position();
        setPosition(newMousePosition);
    }

    QQuickWindow::mouseMoveEvent(e);
}

void MyWindow::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        m_initialMouseClickPos = e->pos();
        m_move = true;
    }

    QQuickWindow::mousePressEvent(e);
}

void MyWindow::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        m_move = false;
    }

    QQuickWindow::mouseReleaseEvent(e);
}
Jacob Krieg
  • 2,834
  • 15
  • 68
  • 140

2 Answers2

1

The slider isn't a widget, and it doesn't process events like widgets do :(

To implement drag on a QQuickWindow, you could have a mouse area in Qt Quick, behind the controls, and have it forward drags to a helper object that then drags the window around.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks for the interest! *you could have a mouse area in Qt Quick, behind the controls, and have it forward drags to a helper object that then drags the window around* - yes but this sounds very hacky...`QQuickWindow` has all the mouse events one needs to elegantly implement this kind of functionality...and if you want to extend this to window resize functionality things get more complex... – Jacob Krieg Sep 13 '16 at 14:45
  • `QQuickWindow` presumably doesn't know anything about the geometry of Qt Quick controls, this is handled internally by Qt Quick... – Kuba hasn't forgotten Monica Sep 13 '16 at 17:53
1

The problem is that QQuickWindow::mouseXxxEvent() delivers the event to the item it belongs to. You have overridden the event handlers, do your handling first, and then pass on the event to QQuickWindow. Therefore the Slider receives the events right after you have done your custom event handling. Either don't call the base class implementation when you don`t want it to deliver the event to items, or call the base class implementation first and do your custom handling afterwards only if the event was not accepted or so.

void MyWindow::mousePressEvent(QMouseEvent *e)
{
    QQuickWindow::mousePressEvent(e);

    if (!e->isAccepted() && e->button() == Qt::LeftButton)
    {
        m_initialMouseClickPos = e->pos();
        m_move = true;
    }

}
jpnurmi
  • 5,716
  • 2
  • 21
  • 37
  • But can you tell me, why the exact same code(w/o your fix) but using `QtWidgets` works fine? Why with widgets if I do the handling last, everything works fine? I.e. if I have a QWidget and I override all these event handlers and then I put a QPushButton on top of it works just like it works your code(i.e. when you click the QPushButton the event doesn't get to the parent widget). I'd really need to know this because it seems that the event propagation paradigm is different from widgets to qtquick. Thanks! – Jacob Krieg Sep 13 '16 at 23:27
  • 1
    `QWindow` is the lower level entry point for events from the window system, and `QWidget` is not a `QWindow`. The underlying `QWidgetWindow`, which routes the event to the appropriate `QWidget`, is "hidden" from you. Even though you can access it via `QWidget::windowHandle()`, with widgets you override event handlers on `QWidget` level, not on `QWindow` level. You can think of `QQuickWindow`'s event delivery similar to that of `QGraphicsView`. The event handlers of the view deliver the events to its scene/items. – jpnurmi Sep 14 '16 at 08:01
  • 1
    Or to put it differently, when a `QWindow` receives a mouse event, nothing has processed it yet. When a `QWidget` receives a mouse event, its children have been already processed and none of them accepted it. If any children had accepted the event, the widget wouldn't be receiving it. – jpnurmi Sep 14 '16 at 08:12