tdf#125517 Qt5 implement a minimal Qt5ObjectWindow

Since we can't use an input and visual transparent widget, like a
GtkGrid, we have to implement input forwarding in the QWindow.

Using a Qt5Widget directly results in other problems on Qt 5.12+,
like these warnings (depending on the tested Qt::WA_* flags):
* Attempted flush to non-raster surface QWidgetWindow(0xa386c10,
  name="QWidgetClassWindow") of type QSurface::OpenGLSurface
  (consider using Qt::WA_PaintOnScreen to exclude from
  backingstore sync)
* QWidget::paintEngine: Should no longer be called

So the current QWidget::createWindowContainer has to stay and key
and mouse handling must be implemented as in Qt5Widget. And the
QWindow is strangely not accessible through the windowHandle() of
the container QWwidget.

As a result this patch is mostly boilerplate code, publishing the
Qt5Widget mouse and key handling as static functions.

Change-Id: I5be5f5fa1379c6bdefab0f96604251801c252b38
Reviewed-on: https://gerrit.libreoffice.org/73566
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
(cherry picked from commit 25edbded9946801effd117b9c46de0f8b4bc5632)
Reviewed-on: https://gerrit.libreoffice.org/73917
diff --git a/vcl/inc/qt5/Qt5Object.hxx b/vcl/inc/qt5/Qt5Object.hxx
index 88cd8ba..e27d549 100644
--- a/vcl/inc/qt5/Qt5Object.hxx
+++ b/vcl/inc/qt5/Qt5Object.hxx
@@ -24,10 +24,10 @@

#include <QtCore/QObject>
#include <QtGui/QRegion>
#include <QtGui/QWindow>

class Qt5Frame;
class QWidget;
class QWindow;

class Qt5Object : public QObject, public SalObject
{
@@ -44,6 +44,10 @@
public:
    Qt5Object(Qt5Frame* pParent, bool bShow);

    Qt5Frame* frame() const { return m_pParent; }
    QWidget* widget() const { return m_pQWidget; }
    QWindow* windowHandle() const { return m_pQWindow; }

    virtual void ResetClipRegion() override;
    virtual void BeginSetClipRegion(sal_uInt32 nRects) override;
    virtual void UnionClipRegion(long nX, long nY, long nWidth, long nHeight) override;
@@ -57,4 +61,18 @@
    virtual const SystemEnvData* GetSystemData() const override { return &m_aSystemData; }
};

class Qt5ObjectWindow : public QWindow
{
    Qt5Object& m_rParent;

    bool event(QEvent*) override;
    void mousePressEvent(QMouseEvent*) override;
    void mouseReleaseEvent(QMouseEvent*) override;
    // keyPressEvent(QKeyEvent*) is handled via event(QEvent*); see comment in Qt5Widget::event
    void keyReleaseEvent(QKeyEvent*) override;

public:
    explicit Qt5ObjectWindow(Qt5Object& rParent);
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/qt5/Qt5Widget.hxx b/vcl/inc/qt5/Qt5Widget.hxx
index ce4e068..8ead4b3 100644
--- a/vcl/inc/qt5/Qt5Widget.hxx
+++ b/vcl/inc/qt5/Qt5Widget.hxx
@@ -27,17 +27,6 @@

class Qt5Frame;
class Qt5Object;
class QEvent;
class QFocusEvent;
class QInputMethodEvent;
class QKeyEvent;
class QMouseEvent;
class QMoveEvent;
class QPaintEvent;
class QResizeEvent;
class QShowEvent;
class QWheelEvent;
class QVariant;

class Qt5Widget : public QWidget
{
@@ -48,9 +37,15 @@
    int m_nDeltaX;
    int m_nDeltaY;

    bool handleKeyEvent(QKeyEvent*, bool);
    void handleMouseButtonEvent(QMouseEvent*, bool);
    void commitText(const QString& aText) const;
    enum class ButtonKeyState
    {
        Pressed,
        Released
    };

    static void commitText(Qt5Frame&, const QString& aText);
    static bool handleKeyEvent(Qt5Frame&, const QWidget&, QKeyEvent*, const ButtonKeyState);
    static void handleMouseButtonEvent(const Qt5Frame&, QMouseEvent*, const ButtonKeyState);

    virtual bool event(QEvent*) override;

@@ -84,6 +79,28 @@
    Qt5Frame& getFrame() const { return m_rFrame; }
    void startDrag(sal_Int8 nSourceActions);
    void endExtTextInput();

    static bool handleEvent(Qt5Frame&, const QWidget&, QEvent*);
    // key events might be propagated further down => call base on false
    static inline bool handleKeyReleaseEvent(Qt5Frame&, const QWidget&, QKeyEvent*);
    // mouse events are always accepted
    static inline void handleMousePressEvent(const Qt5Frame&, QMouseEvent*);
    static inline void handleMouseReleaseEvent(const Qt5Frame&, QMouseEvent*);
};

bool Qt5Widget::handleKeyReleaseEvent(Qt5Frame& rFrame, const QWidget& rWidget, QKeyEvent* pEvent)
{
    return handleKeyEvent(rFrame, rWidget, pEvent, ButtonKeyState::Released);
}

void Qt5Widget::handleMousePressEvent(const Qt5Frame& rFrame, QMouseEvent* pEvent)
{
    handleMouseButtonEvent(rFrame, pEvent, ButtonKeyState::Pressed);
}

void Qt5Widget::handleMouseReleaseEvent(const Qt5Frame& rFrame, QMouseEvent* pEvent)
{
    handleMouseButtonEvent(rFrame, pEvent, ButtonKeyState::Released);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qt5/Qt5Object.cxx b/vcl/qt5/Qt5Object.cxx
index deae5e2..205c8b9 100644
--- a/vcl/qt5/Qt5Object.cxx
+++ b/vcl/qt5/Qt5Object.cxx
@@ -21,10 +21,9 @@
#include <Qt5Object.moc>

#include <Qt5Frame.hxx>
#include <Qt5Widget.hxx>

#include <QtWidgets/QWidget>
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>

Qt5Object::Qt5Object(Qt5Frame* pParent, bool bShow)
    : m_pParent(pParent)
@@ -34,8 +33,9 @@
    if (!m_pParent || !pParent->GetQWidget())
        return;

    m_pQWindow = new QWindow;
    m_pQWindow = new Qt5ObjectWindow(*this);
    m_pQWidget = QWidget::createWindowContainer(m_pQWindow, pParent->GetQWidget());
    m_pQWidget->setAttribute(Qt::WA_NoSystemBackground);

    if (bShow)
        m_pQWidget->show();
@@ -103,4 +103,33 @@

void Qt5Object::SetForwardKey(bool /*bEnable*/) {}

Qt5ObjectWindow::Qt5ObjectWindow(Qt5Object& rParent)
    : m_rParent(rParent)
{
    assert(m_rParent.frame() && m_rParent.frame()->GetQWidget());
}

void Qt5ObjectWindow::mousePressEvent(QMouseEvent* pEvent)
{
    m_rParent.CallCallback(SalObjEvent::ToTop);
    Qt5Widget::handleMousePressEvent(*m_rParent.frame(), pEvent);
}

void Qt5ObjectWindow::mouseReleaseEvent(QMouseEvent* pEvent)
{
    Qt5Widget::handleMouseReleaseEvent(*m_rParent.frame(), pEvent);
}

bool Qt5ObjectWindow::event(QEvent* pEvent)
{
    return Qt5Widget::handleEvent(*m_rParent.frame(), *m_rParent.widget(), pEvent)
           || QWindow::event(pEvent);
}

void Qt5ObjectWindow::keyReleaseEvent(QKeyEvent* pEvent)
{
    if (!Qt5Widget::handleKeyReleaseEvent(*m_rParent.frame(), *m_rParent.widget(), pEvent))
        QWindow::keyReleaseEvent(pEvent);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qt5/Qt5OpenGLContext.cxx b/vcl/qt5/Qt5OpenGLContext.cxx
index 6f095e7..a33f7abd 100644
--- a/vcl/qt5/Qt5OpenGLContext.cxx
+++ b/vcl/qt5/Qt5OpenGLContext.cxx
@@ -147,5 +147,6 @@
        InitChildWindow(m_pChildWindow.get());
    }

    m_pWindow = static_cast<Qt5Object*>(m_pChildWindow->ImplGetWindowImpl()->mpSysObj)->m_pQWindow;
    m_pWindow
        = static_cast<Qt5Object*>(m_pChildWindow->ImplGetWindowImpl()->mpSysObj)->windowHandle();
}
diff --git a/vcl/qt5/Qt5Widget.cxx b/vcl/qt5/Qt5Widget.cxx
index 27ce382..b46b48b 100644
--- a/vcl/qt5/Qt5Widget.cxx
+++ b/vcl/qt5/Qt5Widget.cxx
@@ -120,7 +120,8 @@
    m_rFrame.CallCallback(SalEvent::Resize, nullptr);
}

void Qt5Widget::handleMouseButtonEvent(QMouseEvent* pEvent, bool bReleased)
void Qt5Widget::handleMouseButtonEvent(const Qt5Frame& rFrame, QMouseEvent* pEvent,
                                       const ButtonKeyState eState)
{
    SalMouseEvent aEvent;
    switch (pEvent->button())
@@ -144,16 +145,19 @@
    aEvent.mnCode = GetKeyModCode(pEvent->modifiers()) | GetMouseModCode(pEvent->buttons());

    SalEvent nEventType;
    if (bReleased)
        nEventType = SalEvent::MouseButtonUp;
    else
    if (eState == ButtonKeyState::Pressed)
        nEventType = SalEvent::MouseButtonDown;
    m_rFrame.CallCallback(nEventType, &aEvent);
    else
        nEventType = SalEvent::MouseButtonUp;
    rFrame.CallCallback(nEventType, &aEvent);
}

void Qt5Widget::mousePressEvent(QMouseEvent* pEvent) { handleMouseButtonEvent(pEvent, false); }
void Qt5Widget::mousePressEvent(QMouseEvent* pEvent) { handleMousePressEvent(m_rFrame, pEvent); }

void Qt5Widget::mouseReleaseEvent(QMouseEvent* pEvent) { handleMouseButtonEvent(pEvent, true); }
void Qt5Widget::mouseReleaseEvent(QMouseEvent* pEvent)
{
    handleMouseReleaseEvent(m_rFrame, pEvent);
}

void Qt5Widget::mouseMoveEvent(QMouseEvent* pEvent)
{
@@ -405,7 +409,7 @@
    return nCode;
}

void Qt5Widget::commitText(const QString& aText) const
void Qt5Widget::commitText(Qt5Frame& rFrame, const QString& aText)
{
    SalExtTextInputEvent aInputEvent;
    aInputEvent.mpTextAttr = nullptr;
@@ -414,19 +418,21 @@
    aInputEvent.mnCursorPos = aInputEvent.maText.getLength();

    SolarMutexGuard aGuard;
    vcl::DeletionListener aDel(&m_rFrame);
    m_rFrame.CallCallback(SalEvent::ExtTextInput, &aInputEvent);
    vcl::DeletionListener aDel(&rFrame);
    rFrame.CallCallback(SalEvent::ExtTextInput, &aInputEvent);
    if (!aDel.isDeleted())
        m_rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
        rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
}

bool Qt5Widget::handleKeyEvent(QKeyEvent* pEvent, bool bDown)
bool Qt5Widget::handleKeyEvent(Qt5Frame& rFrame, const QWidget& rWidget, QKeyEvent* pEvent,
                               const ButtonKeyState eState)
{
    sal_uInt16 nCode = GetKeyCode(pEvent->key(), pEvent->modifiers());
    if (bDown && nCode == 0 && !pEvent->text().isEmpty()
        && testAttribute(Qt::WA_InputMethodEnabled))
    if (eState == ButtonKeyState::Pressed && nCode == 0 && !pEvent->text().isEmpty()
        && rWidget.testAttribute(Qt::WA_InputMethodEnabled))
    {
        commitText(pEvent->text());
        commitText(rFrame, pEvent->text());
        pEvent->accept();
        return true;
    }

@@ -439,14 +445,16 @@
    QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);

    bool bStopProcessingKey;
    if (bDown)
        bStopProcessingKey = m_rFrame.CallCallback(SalEvent::KeyInput, &aEvent);
    if (eState == ButtonKeyState::Pressed)
        bStopProcessingKey = rFrame.CallCallback(SalEvent::KeyInput, &aEvent);
    else
        bStopProcessingKey = m_rFrame.CallCallback(SalEvent::KeyUp, &aEvent);
        bStopProcessingKey = rFrame.CallCallback(SalEvent::KeyUp, &aEvent);
    if (bStopProcessingKey)
        pEvent->accept();
    return bStopProcessingKey;
}

bool Qt5Widget::event(QEvent* pEvent)
bool Qt5Widget::handleEvent(Qt5Frame& rFrame, const QWidget& rWidget, QEvent* pEvent)
{
    if (pEvent->type() == QEvent::ShortcutOverride)
    {
@@ -459,17 +467,22 @@
        // and if it's handled - disable the shortcut, it should have been activated.
        // Don't process keyPressEvent generated after disabling shortcut since it was handled here.
        // If event is not handled, don't accept it and let Qt activate related shortcut.
        if (handleKeyEvent(static_cast<QKeyEvent*>(pEvent), true))
            pEvent->accept();
        if (handleKeyEvent(rFrame, rWidget, static_cast<QKeyEvent*>(pEvent),
                           ButtonKeyState::Pressed))
            return true;
    }
    return false;
}

    return QWidget::event(pEvent);
bool Qt5Widget::event(QEvent* pEvent)
{
    return handleEvent(m_rFrame, *this, pEvent) || QWidget::event(pEvent);
}

void Qt5Widget::keyReleaseEvent(QKeyEvent* pEvent)
{
    if (handleKeyEvent(pEvent, false))
        pEvent->accept();
    if (!handleKeyReleaseEvent(m_rFrame, *this, pEvent))
        QWidget::keyReleaseEvent(pEvent);
}

void Qt5Widget::focusInEvent(QFocusEvent*) { m_rFrame.CallCallback(SalEvent::GetFocus, nullptr); }
@@ -519,7 +532,7 @@
void Qt5Widget::inputMethodEvent(QInputMethodEvent* pEvent)
{
    if (!pEvent->commitString().isEmpty())
        commitText(pEvent->commitString());
        commitText(m_rFrame, pEvent->commitString());
    else
    {
        SalExtTextInputEvent aInputEvent;