0

As it stands, on Wayland, widgets without parents can't be moved with the move command. See this limitation here and another discussion here. To move a top level widget we could use an "interactive session" provided by the platform that supports it, through the QWindow.startSystemMove call.

The problem is by depending on something other than Qt to handle the move, on Wayland, the Qt MouseEvents do not work properly, after the system move.

I added a code snippet below to illustrate. After the startSystemMove, the MouseRelease event is not fired. Hover events also don't work on the child widgets until the parent widget receives a right-click or 3rd mouse button click.

I am trying to make hover events on child widgets work, after startSystemMove is done. I've tried simulating QMouseRelease event through the application right after a startSystemMove is done, but this doesn't work as well. Is there something I'm missing?

import sys
from PySide6 import QtWidgets, QtCore, QtGui
import PySide6.QtWidgets

Qt = QtCore.Qt
QMouseEvent = QtGui.QMouseEvent
QWidget = QtWidgets.QWidget
QEvent = QtCore.QEvent
CursorShape = Qt.CursorShape
QApplication = QtWidgets.QApplication
WidgetAttributes = Qt.WidgetAttribute
WindowTypes = Qt.WindowType
QPoint = QtCore.QPoint
QVBoxLayout = QtWidgets.QVBoxLayout
QObject = QtCore.QObject
Signal = QtCore.Signal
Slot = QtCore.Slot
QFrame = QtWidgets.QFrame
QByteArray = QtCore.QByteArray

class QMovableWidget(QWidget):
    def __init__(self, p, f,):
        # type: (QWidget | None, WindowTypes,) -> None
        super().__init__(p, f)

        self.setAttribute(Qt.WidgetAttribute.WA_MouseTracking, True)
        self.setAttribute(Qt.WidgetAttribute.WA_NativeWindow, True)
        self.___systemMove = False

    def event(self, ev):
        # type: (QEvent | QMouseEvent) -> bool
        if (
            ev.type() == QEvent.Type.MouseButtonPress
            and ev.button() == Qt.MouseButton.LeftButton
        ):
            print(self.window().windowHandle().startSystemMove())
            self.___systemMove = True
            print("Start System Move")
        

        if ev.type() == QEvent.Type.WindowActivate:
            if self.___systemMove:
                print("System Move Done")             
                self.___systemMove = False
                self.updateGeometry()

        return super().event(ev)


class OuterWidget(QMovableWidget):
    def __init__(self, p: QWidget | None, f: WindowTypes,) -> None:
        super().__init__(p, f,)
        self.setAttribute(WidgetAttributes.WA_Hover, True)
        self.setCursor(CursorShape.ArrowCursor)
    
    def event(self, ev: QEvent | QMouseEvent) -> bool:
        if ev.type() == QEvent.Type.HoverEnter:
            print("OuterWidget hover enter")

        elif (ev.type() == QEvent.Type.HoverLeave):
            print("OuterWidget hover leave")

        return super().event(ev)
    
    def mousePressEvent(self, event: QMouseEvent) -> None:
        print("OuterWidget", "ms press")
        return super().mousePressEvent(event)

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        print("OuterWidget", "ms release")
        return super().mouseReleaseEvent(event)

    # def mouseMoveEvent(self, event: QMouseEvent) -> None:
    #     global count

    #     print("OuterWidget", "ms move", count, event)
    #     count += 1
    #     return super().mouseMoveEvent(event)

    def nativeEvent(self, eventType: QByteArray | bytes, message: int) -> object:
        print("OuterWidget", "Native Event", eventType, message)
        return super().nativeEvent(eventType, message)


class InnerWidget(QFrame):
    def __init__(self, parent: QWidget | None = ..., f: WindowTypes = ...) -> None:
        super().__init__(parent, f)
        self.setAttribute(WidgetAttributes.WA_MouseTracking, True)
        self.setAttribute(WidgetAttributes.WA_Hover, True)
        self.setCursor(CursorShape.PointingHandCursor)

    def event(self, e: QEvent) -> bool:
        if (e.type() == QEvent.Type.HoverEnter):
            print("InnerWidget hover enter")

        elif (e.type() == QEvent.Type.HoverLeave):
            print("InnerWidget hover leave")

        return super().event(e)
    
    def nativeEvent(self, eventType: QByteArray | bytes, message: int) -> object:
        print("InnerWidget", "Native Event", eventType, message)
        return super().nativeEvent(eventType, message)
    
    def mousePressEvent(self, event: QMouseEvent) -> None:
        print("InnerWidget", "ms press")
        return super().mousePressEvent(event)

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        print("InnerWidget", "ms release")
        return super().mouseReleaseEvent(event)
    
    # def mouseMoveEvent(self, event: QMouseEvent) -> None:
    #     global count

    #     print("InnerWidget", "ms move", count, event)
    #     event.accept()
    #     count += 1
    #     return super().mouseMoveEvent(event)



count = 1

if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")

    window = OuterWidget(
        None, WindowTypes.Window | WindowTypes.FramelessWindowHint
    )
    window.resize(300, 300)

    l = QVBoxLayout()
    window.setLayout(l)
    w = InnerWidget(window, WindowTypes.Widget)
    w.setStyleSheet("background-color: red")
    l.addWidget(w)

    window.show()

    sys.exit(app.exec())
razor_chk
  • 77
  • 2
  • 9
  • 1
    Probably related to [QTBUG-68501](//bugreports.qt.io/browse/QTBUG-68501) and [QTBUG-69716](//bugreports.qt.io/browse/QTBUG-69716), maybe due to changes added to work around such issues. It seems to work fine under X11 (at least for a few quick tests I made), but I don't use Wayland so I cannot properly test it. Creating a synthetic mouse release event may be a work around, but you have to be very careful about that: you must ensure that the issue actually only happens on Wayland and the current Qt version is actually affected by the issue (in case a future version fixes this). – musicamante Jul 11 '23 at 22:11
  • And there could also be the problem that the issue is actually caused by your current Wayland version. You should try to make some more appropriate testing under different OS conditions, and also consider ignoring the mouse press event and/or grabbing the mouse before starting the system move call. – musicamante Jul 11 '23 at 22:13

0 Answers0