Commit b7ab90b7 authored by dmMaze's avatar dmMaze
Browse files

fix weird widget behaviour on linux

parent 70fa2c1c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -130,6 +130,7 @@ def main():
    if qtpy.API_NAME[-1] == '6':
        C.FLAG_QT6 = True
    else:
        C.FLAG_QT6 = False
        QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) #enable highdpi scaling
        QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) #use highdpi icons
        QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
+25 −12
Original line number Diff line number Diff line
@@ -151,6 +151,8 @@ class Canvas(QGraphicsScene):
    textstack_changed = Signal()
    drop_open_folder = Signal(str)

    context_menu_requested = Signal(QPoint)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.scale_factor = 1.
@@ -172,6 +174,9 @@ class Canvas(QGraphicsScene):
        self.gv.canvas = self
        self.gv.setAcceptDrops(True)

        self.gv.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
        self.context_menu_requested.connect(self.createCustomContextMenu)
        
        if not C.FLAG_QT6:
            # mitigate https://bugreports.qt.io/browse/QTBUG-93417
            # produce blurred result, saving imgs remain unaffected
@@ -546,17 +551,20 @@ class Canvas(QGraphicsScene):

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
        btn = event.button()

        self.hide_rubber_band()

        Qt.MouseButton.LeftButton
        if btn == Qt.MouseButton.MiddleButton:
            self.mid_btn_pressed = False
        elif self.creating_textblock:
            btn = 0 if btn == Qt.MouseButton.LeftButton else 1
            return self.endCreateTextblock(btn=btn)
        elif btn == Qt.MouseButton.RightButton:
        if self.creating_textblock:
            tgt = 0 if btn == Qt.MouseButton.LeftButton else 1
            self.endCreateTextblock(btn=tgt)
        if btn == Qt.MouseButton.RightButton:
            if self.stroke_img_item is not None:
                self.finish_erasing.emit(self.stroke_img_item)
            if self.rubber_band.isVisible():
                self.rubber_band.hide()
                self.rubber_band_origin = None
            if self.textEditMode():
                self.context_menu_requested.emit(event.screenPos())
        elif btn == Qt.MouseButton.LeftButton:
            if self.stroke_img_item is not None:
                self.finish_painting.emit(self.stroke_img_item)
@@ -631,8 +639,8 @@ class Canvas(QGraphicsScene):
    def setTextBlockMode(self, mode: bool):
        self.textblock_mode = mode

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent):
        if self.textEditMode():
    def createCustomContextMenu(self, pos: QPoint):
        if self.textEditMode() and not self.creating_textblock:
            menu = QMenu(self.gv)
            copy_act = menu.addAction(self.tr("Copy"))
            paste_act = menu.addAction(self.tr("Paste"))
@@ -648,16 +656,16 @@ class Canvas(QGraphicsScene):
            ocr_translate_act = menu.addAction(self.tr("OCR and translate"))
            ocr_translate_inpaint_act = menu.addAction(self.tr("OCR, translate and inpaint"))

            rst = menu.exec_(event.screenPos())
            rst = menu.exec(pos)
            
            if rst == delete_act:
                self.delete_textblks.emit(0)
            elif rst == delete_recover_act:
                self.delete_textblks.emit(1)
            elif rst == copy_act:
                self.copy_textblks.emit(event.scenePos())
                self.copy_textblks.emit(pos.toPointF())
            elif rst == paste_act:
                self.paste_textblks.emit(event.scenePos())
                self.paste_textblks.emit(pos.toPointF())
            elif rst == format_act:
                self.format_textblks.emit()
            elif rst == layout_act:
@@ -673,6 +681,11 @@ class Canvas(QGraphicsScene):
            elif rst == ocr_translate_inpaint_act:
                self.run_blktrans.emit(2)

    def hide_rubber_band(self):
        if self.rubber_band.isVisible():
            self.rubber_band.hide()
            self.rubber_band_origin = None
    
    def on_hide_canvas(self):
        self.clear_states()

+16 −11
Original line number Diff line number Diff line
@@ -17,16 +17,17 @@ class LinuxFramelessWindow(QWidget):
        super().__init__(parent=parent)
        self.windowEffect = LinuxWindowEffect(self)
        # self.titleBar = TitleBar(self)
        self._isResizeEnabled = True

        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowType.FramelessWindowHint)
        QCoreApplication.instance().installEventFilter(self)

        # self.titleBar.raise_()
        # self.resize(500, 500)
        self.resize(500, 500)

    # def resizeEvent(self, e):
    #     super().resizeEvent(e)
    def resizeEvent(self, e):
        super().resizeEvent(e)
        # self.titleBar.resize(self.width(), self.titleBar.height())

    # def setTitleBar(self, titleBar):
@@ -42,21 +43,25 @@ class LinuxFramelessWindow(QWidget):
    #     self.titleBar.setParent(self)
    #     self.titleBar.raise_()

    def setResizeEnabled(self, isEnabled: bool):
        """ set whether resizing is enabled """
        self._isResizeEnabled = isEnabled

    def eventFilter(self, obj, event):
        et = event.type()
        if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove:
        if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove or not self._isResizeEnabled:
            return False

        edges = 0
        edges = Qt.Edge(0)
        pos = event.globalPosition().toPoint() - self.pos()
        if pos.x() < self.BORDER_WIDTH:
            edges = edges | Qt.Edge.LeftEdge if edges != 0 else Qt.Edge.LeftEdge
            edges |= Qt.Edge.LeftEdge
        if pos.x() >= self.width()-self.BORDER_WIDTH:
            edges = edges | Qt.Edge.RightEdge if edges != 0 else Qt.Edge.RightEdge 
            edges |= Qt.Edge.RightEdge
        if pos.y() < self.BORDER_WIDTH:
            edges = edges | Qt.Edge.TopEdge if edges != 0 else Qt.Edge.TopEdge
            edges |= Qt.Edge.TopEdge
        if pos.y() >= self.height()-self.BORDER_WIDTH:
            edges = edges | Qt.Edge.BottomEdge if edges != 0 else Qt.Edge.BottomEdge 
            edges |= Qt.Edge.BottomEdge

        # change cursor
        if et == QEvent.Type.MouseMove and self.windowState() == Qt.WindowState.WindowNoState:
@@ -71,7 +76,7 @@ class LinuxFramelessWindow(QWidget):
            else:
                self.setCursor(Qt.CursorShape.ArrowCursor)

        elif obj in (self, self.titleBar) and et == QEvent.Type.MouseButtonPress and edges:
        elif obj == self and et == QEvent.Type.MouseButtonPress and edges:
            LinuxMoveResize.starSystemResize(self, event.globalPosition(), edges)

        return super().eventFilter(obj, event)
+2 −131
Original line number Diff line number Diff line
# coding: utf-8
from enum import Enum

import os
import xcffib as xcb
from PyQt6.QtCore import QPointF, Qt
from xcffib.xproto import (ButtonIndex, ButtonMask, ButtonReleaseEvent,
                           ClientMessageData, ClientMessageEvent, EventMask,
                           xprotoExtension)


class WindowMessage(Enum):
    """ Window message enum class """
    # refer to: https://specifications.freedesktop.org/wm-spec/1.1/x170.html
    _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0
    _NET_WM_MOVERESIZE_SIZE_TOP = 1
    _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2
    _NET_WM_MOVERESIZE_SIZE_RIGHT = 3
    _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4
    _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5
    _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6
    _NET_WM_MOVERESIZE_SIZE_LEFT = 7
    _NET_WM_MOVERESIZE_MOVE = 8
    _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9
    _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10
    _NET_WM_MOVERESIZE_CANCEL = 11


class LinuxMoveResize:
    """ Tool class for moving and resizing window """

    moveResizeAtom = None

    @classmethod
    def sendButtonReleaseEvent(cls, window, globalPos):
        """ send button release event

        Parameters
        ----------
        window: QWidget
            window to be moved or resized

        globalPos: QPoint
            the global point of mouse release event
        """
        globalPos = QPointF(QPointF(globalPos) *
                            window.devicePixelRatio()).toPoint()
        pos = window.mapFromGlobal(globalPos)

        # open the connection to X server
        conn = xcb.connect(os.environ.get('DISPLAY'))
        windowId = int(window.winId())
        xproto = xprotoExtension(conn)

        # refer to: https://www.x.org/releases/X11R7.5/doc/libxcb/tutorial/
        event = ButtonReleaseEvent.synthetic(
            detail=ButtonIndex._1,
            time=xcb.CurrentTime,
            root=conn.get_setup().roots[0].root,
            event=windowId,
            child=xcb.NONE,
            root_x=globalPos.x(),
            root_y=globalPos.y(),
            event_x=pos.x(),
            event_y=pos.y(),
            state=ButtonMask._1,
            same_screen=True,
        )
        xproto.SendEvent(True, windowId, EventMask.ButtonRelease, event.pack())
        conn.flush()

    @classmethod
    def startSystemMoveResize(cls, window, globalPos, message):
        """ resize window

        Parameters
        ----------
        window: QWidget
            window to be moved or resized

        globalPos: QPoint
            the global point of mouse release event

        message: int
            window message
        """
        cls.sendButtonReleaseEvent(window, globalPos)

        globalPos = QPointF(QPointF(globalPos) *
                            window.devicePixelRatio()).toPoint()

        # open the connection to X server
        conn = xcb.connect(os.environ.get('DISPLAY'))
        xproto = xprotoExtension(conn)

        if not cls.moveResizeAtom:
            cls.moveResizeAtom = xproto.InternAtom(
                False, len("_NET_WM_MOVERESIZE"), "_NET_WM_MOVERESIZE").reply().atom

        union = ClientMessageData.synthetic([
            globalPos.x(),
            globalPos.y(),
            message,
            ButtonIndex._1,
            0
        ], "I"*5)
        event = ClientMessageEvent.synthetic(
            format=32,
            window=int(window.winId()),
            type=cls.moveResizeAtom,
            data=union
        )
        xproto.UngrabPointer(xcb.CurrentTime)
        xproto.SendEvent(
            False,
            conn.get_setup().roots[0].root,
            EventMask.SubstructureRedirect | EventMask.SubstructureNotify,
            event.pack()
        )
        conn.flush()

    @classmethod
    def startSystemMove(cls, window, globalPos):
        """ move window """
        cls.startSystemMoveResize(
            window, globalPos, WindowMessage._NET_WM_MOVERESIZE_MOVE.value)
        window.windowHandle().startSystemMove()

    @classmethod
    def starSystemResize(cls, window, globalPos, edges):
@@ -140,17 +24,4 @@ class LinuxMoveResize:
        edges: `Qt.Edges`
            window edges
        """
        if not edges:
            return

        messageMap = {
            Qt.Edge.TopEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOP,
            Qt.Edge.TopEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPLEFT,
            Qt.Edge.TopEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPRIGHT,
            Qt.Edge.BottomEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOM,
            Qt.Edge.BottomEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT,
            Qt.Edge.BottomEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT,
            Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_LEFT,
            Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_RIGHT,
        }
        cls.startSystemMoveResize(window, globalPos, messageMap[edges].value)
        window.windowHandle().startSystemResize(edges)
 No newline at end of file