Commit a4a28b3f authored by dmMaze's avatar dmMaze
Browse files

frameless window refactory for macos

parent 1a1a6ecd
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
import sys

if sys.platform == "win32":
    from .windows import AcrylicWindow
    from .windows import WindowsFramelessWindow as FramelessWindow
    from .windows import WindowsWindowEffect as WindowEffect
    from .win_frameless_window import AcrylicWindow
    from .win_frameless_window import WindowsFramelessWindow as FramelessWindow
    from .win_frameless_window import WindowsWindowEffect as WindowEffect
elif sys.platform == "darwin":
    from .mac import AcrylicWindow
    from .mac import MacFramelessWindow as FramelessWindow
    from .mac import MacWindowEffect as WindowEffect
    raise Exception(f'Please update to PySide6/PyQt6')
else:
    from .linux import LinuxFramelessWindow as FramelessWindow
    from .linux import LinuxWindowEffect as WindowEffect
+0 −81
Original line number Diff line number Diff line
# coding:utf-8
import Cocoa
import objc
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtWidgets import QWidget

from .window_effect import MacWindowEffect


class MacFramelessWindow(QWidget):
    """ Frameless window for Linux system """

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.windowEffect = MacWindowEffect(self)
        # must enable acrylic effect before creating title bar
        if isinstance(self, AcrylicWindow):
            self.windowEffect.setAcrylicEffect(self.winId())


        view = objc.objc_object(c_void_p=self.winId().__int__())
        self.__nsWindow = view.window()

        # hide system title bar
        self.__hideSystemTitleBar()

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

    # def setTitleBar(self, titleBar):
    #     """ set custom title bar

    #     Parameters
    #     ----------
    #     titleBar: TitleBar
    #         title bar
    #     """
    #     self.titleBar.deleteLater()
    #     self.titleBar = titleBar
    #     self.titleBar.setParent(self)
    #     self.titleBar.raise_()

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

    def paintEvent(self, e):
        super().paintEvent(e)
        self.__nsWindow.setTitlebarAppearsTransparent_(True)

    def changeEvent(self, event):
        super().changeEvent(event)
        if event.type() == QEvent.WindowStateChange:
            self.__hideSystemTitleBar()

    def __hideSystemTitleBar(self):
        # extend view to title bar region
        self.__nsWindow.setStyleMask_(
            self.__nsWindow.styleMask() | Cocoa.NSFullSizeContentViewWindowMask)
        self.__nsWindow.setTitlebarAppearsTransparent_(True)

        # disable the moving behavior of system
        self.__nsWindow.setMovableByWindowBackground_(False)
        self.__nsWindow.setMovable_(False)

        # hide title bar buttons and title
        self.__nsWindow.setShowsToolbarButton_(False)
        self.__nsWindow.setTitleVisibility_(Cocoa.NSWindowTitleHidden)
        self.__nsWindow.standardWindowButton_(Cocoa.NSWindowCloseButton).setHidden_(True)
        self.__nsWindow.standardWindowButton_(Cocoa.NSWindowZoomButton).setHidden_(True)
        self.__nsWindow.standardWindowButton_(Cocoa.NSWindowMiniaturizeButton).setHidden_(True)


class AcrylicWindow(MacFramelessWindow):
    """ A frameless window with acrylic effect """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.windowEffect.setAcrylicEffect(self.winId())
        self.setStyleSheet("background: transparent")
+0 −139
Original line number Diff line number Diff line
# coding:utf-8
import objc
import Cocoa
from PyQt5.QtWidgets import QMacCocoaViewContainer
from ..utils.mac_utils import getNSWindow

class MacWindowEffect:
    """ Mac OS window effect """

    def __init__(self, window):
        self.window = window

    def setAcrylicEffect(self, hWnd, gradientColor="F2F2F230", isEnableShadow=True, animationId=0):
        """ set acrylic effect for window

        Parameter
        ----------
        hWnd: int or `sip.voidptr`
            window handle

        gradientColor: str
            hexadecimal acrylic mixed color, corresponding to RGBA components

        isEnableShadow: bool
            whether to enable window shadow

        animationId: int
            turn on blur animation or not
        """
        frame = Cocoa.NSMakeRect(
            0, 0, self.window.width(), self.window.height())
        visualEffectView = Cocoa.NSVisualEffectView.new()
        visualEffectView.setAutoresizingMask_(
            Cocoa.NSViewWidthSizable | Cocoa.NSViewHeightSizable)  # window resizable
        visualEffectView.setFrame_(frame)
        visualEffectView.setState_(Cocoa.NSVisualEffectStateActive)

        # https://developer.apple.com/documentation/appkit/nsvisualeffectmaterial
        visualEffectView.setMaterial_(Cocoa.NSVisualEffectMaterialPopover)
        visualEffectView.setBlendingMode_(
            Cocoa.NSVisualEffectBlendingModeBehindWindow)

        nsWindow = getNSWindow(self.window.winId())
        content = nsWindow.contentView()
        container = QMacCocoaViewContainer(0, self.window)
        content.addSubview_positioned_relativeTo_(
            visualEffectView, Cocoa.NSWindowBelow, container)

    def setMicaEffect(self, hWnd):
        """ Add mica effect to the window (Win11 only)

        Parameters
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        self.setAcrylicEffect(hWnd)

    def setAeroEffect(self, hWnd):
        """ add Aero effect to the window

        Parameter
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        self.setAcrylicEffect(hWnd)

    def setTransparentEffect(self, hWnd):
        """ set transparent effect for window

        Parameters
        ----------
        hWnd : int or `sip.voidptr`
            Window handle
        """
        pass

    def removeBackgroundEffect(self, hWnd):
        """ Remove background effect

        Parameters
        ----------
        hWnd : int or `sip.voidptr`
            Window handle
        """
        pass

    def addShadowEffect(self, hWnd):
        """ add shadow to window

        Parameter
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        getNSWindow(hWnd).setHasShadow_(True)

    def addMenuShadowEffect(self, hWnd):
        """ add shadow to menu

        Parameter
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        self.addShadowEffect(hWnd)

    @staticmethod
    def removeMenuShadowEffect(hWnd):
        """ Remove shadow from pop-up menu

        Parameters
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        getNSWindow(hWnd).setHasShadow_(False)

    def removeShadowEffect(self, hWnd):
        """ Remove shadow from the window

        Parameters
        ----------
        hWnd: int or `sip.voidptr`
            Window handle
        """
        getNSWindow(hWnd).setHasShadow_(False)

    @staticmethod
    def addWindowAnimation(hWnd):
        """ Enables the maximize and minimize animation of the window

        Parameters
        ----------
        hWnd : int or `sip.voidptr`
            Window handle
        """
        pass
+0 −72
Original line number Diff line number Diff line
# coding:utf-8
from ctypes import c_void_p

import Cocoa
import objc
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.QtWidgets import QWidget
from Quartz.CoreGraphics import (CGEventCreateMouseEvent,
                                 kCGEventLeftMouseDown, kCGMouseButtonLeft)

QT_VERSION = tuple(int(v) for v in QT_VERSION_STR.split('.'))


class MacMoveResize:
    """ Tool class for moving and resizing Mac OS window """

    @staticmethod
    def startSystemMove(window: QWidget, globalPos):
        """ resize window

        Parameters
        ----------
        window: QWidget
            window

        globalPos: QPoint
            the global point of mouse release event
        """
        if QT_VERSION >= (5, 15, 0):
            window.windowHandle().startSystemMove()
            return

        nsWindow = getNSWindow(window.winId())

        # send click event
        cgEvent = CGEventCreateMouseEvent(
            None, kCGEventLeftMouseDown, (globalPos.x(), globalPos.y()), kCGMouseButtonLeft)
        clickEvent = Cocoa.NSEvent.eventWithCGEvent_(cgEvent)

        if clickEvent:
            nsWindow.performWindowDragWithEvent_(clickEvent)

        # CFRelease(cgEvent)

    @classmethod
    def starSystemResize(cls, window, globalPos, edges):
        """ resize window

        Parameters
        ----------
        window: QWidget
            window

        globalPos: QPoint
            the global point of mouse release event

        edges: `Qt.Edges`
            window edges
        """
        pass


def getNSWindow(winId):
    """ convert window handle to NSWindow

    Parameters
    ----------
    winId: int or `sip.voidptr`
        window handle
    """
    view = objc.objc_object(c_void_p=c_void_p(int(winId)))
    return view.window()
+7 −7
Original line number Diff line number Diff line
@@ -7,15 +7,15 @@ from platform import platform
import win32api
import win32con
import win32gui
from PyQt5.QtCore import Qt, QSize, QRect
from PyQt5.QtGui import QCloseEvent, QCursor
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow
from qtpy.QtCore import Qt, QSize, QRect
from qtpy.QtGui import QCloseEvent, QCursor
from qtpy.QtWidgets import QApplication, QWidget, QMainWindow

# from ..titlebar import TitleBar
from ... import win32_utils as win_utils
from ...win32_utils import Taskbar, isSystemBorderAccentEnabled, getSystemAccentColor
from ...win_c_structures import LPNCCALCSIZE_PARAMS
from ...win_window_effect import WindowsWindowEffect
from .. import win32_utils as win_utils
from ..win32_utils import Taskbar, isSystemBorderAccentEnabled, getSystemAccentColor
from ..win_c_structures import LPNCCALCSIZE_PARAMS
from ..win_window_effect import WindowsWindowEffect


class WindowsFramelessWindow(QWidget):
Loading