Search code examples
c++qtqmlqt5qtquick2

Qt Quick parent, not the same as the 'id' of the parent


I'm playing a bit with Qt Quick and I wanted to create a title bar for my application. So I inherited QQuickPaintedItem, painted a bit on it, and wanted to use it as a title bar for my Window. I'm using Qt 5.7. This succeeded but only to a certain extent; I'll explain more after the code.

Here is how I did it:

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "mycustomtitlebar.h"

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

    qmlRegisterType<MyCustomTitleBar>("my.custom.lib", 1, 0, "MyCustomTitleBar");

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

    return app.exec();
}

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import my.custom.lib 1.0

Window {
    id: wnd
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MyCustomTitleBar {
        id: titleBar
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.right: parent.right
        height: 100
        hostWidget: wnd
    }

    Rectangle {
        color: "beige"
        anchors.top: titleBar.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

mycustomtitlebar.h

#ifndef MYCUSTOMTITLEBAR_H
#define MYCUSTOMTITLEBAR_H

#include <QQuickPaintedItem>
#include <QPoint>

class MyCustomTitleBar : public QQuickPaintedItem
{
    Q_OBJECT

    Q_PROPERTY(QWindow *hostWidget READ hostWidget WRITE setHostWidget)

public:
    MyCustomTitleBar(QQuickItem *parent = 0);

protected:
    virtual void paint(QPainter *pPainter) Q_DECL_OVERRIDE;
    virtual void mousePressEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;
    virtual void mouseMoveEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;
    virtual void mouseReleaseEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;

private:
    QWindow *hostWidget() const;
    void setHostWidget(QWindow *pHostWidget);

private:
    QWindow *m_pHostWidget;
    QPoint m_initialMousePosition;
    bool m_leftMouseButtonPressed;
};

#endif // MYCUSTOMTITLEBAR_H

mycustomtitlebar.cpp

#include "mycustomtitlebar.h"

#include <QPainter>
#include <QDragMoveEvent>
#include <QWindow>

MyCustomTitleBar::MyCustomTitleBar(QQuickItem *parent)
    : QQuickPaintedItem(parent),
      m_leftMouseButtonPressed(false),
      m_pHostWidget(Q_NULLPTR)
{
    setAcceptedMouseButtons(Qt::AllButtons);
}

void MyCustomTitleBar::paint(QPainter *pPainter)
{
    // Dummy drawing...
    const QRect myRect(10, 10, width() - 20, height() - 20);
    qDebug() << myRect;
    pPainter->drawRect(myRect);
}

void MyCustomTitleBar::mousePressEvent(QMouseEvent *pEvent)
{
    m_leftMouseButtonPressed = true;
    m_initialMousePosition = pEvent->pos();
}

void MyCustomTitleBar::mouseMoveEvent(QMouseEvent *pEvent)
{
    if (m_leftMouseButtonPressed) {
        if (!m_pHostWidget) {
            qDebug() << Q_FUNC_INFO << "Host widget not set. Please set host widget";
            return;
        }

        const QPoint newMousePosition = pEvent->pos() - m_initialMousePosition + m_pHostWidget->position();
        m_pHostWidget->setPosition(newMousePosition);
    }

    QQuickPaintedItem::mouseMoveEvent(pEvent);
}

void MyCustomTitleBar::mouseReleaseEvent(QMouseEvent *pEvent)
{
    m_leftMouseButtonPressed = false;
}

QWindow *MyCustomTitleBar::hostWidget() const
{
    return m_pHostWidget;
}

void MyCustomTitleBar::setHostWidget(QWindow *pHostWidget)
{
    m_pHostWidget = pHostWidget;
}

Now this code works perfectly, I run the application and I am able to click on the title bar and drag, and the whole window moves where I want it to.

BUT, here is the problem: if I change hostWidget: wnd to hostWidget: parent it doesn't work anymore. Can anyone explain why? Because wnd is the parent, after all.

P.S.

I also get this very strange error notification from Qt Creator but the code compiles and runs fine: enter image description here

Why?...


Solution

  • But here is the problem: if I change hostWidget: wnd to hostWidget: parent it doesn't work anymore. Can anyone explain why? Because wnd is the parent after all.

    1. Window QML type instantiates QQuickWindow.

    2. QQuickWindow does not inherit from QQuickItem.

    3. Instead it contains a QQuickItem element accessible through its contentItem() function.

    4. parent property refers to a QQuickItem object.

    Therefore in your example titleBar.parent refers to an element of wnd rather than wnd itself.

    If an attempt is made internally to dynamically cast titleBar.parent to QWindow* before calling MyCustomTitleBar::setHostWidget() it fails because of (2) above (in that case, you should see a corresponding error in the console).