522 lines
17 KiB
Python
522 lines
17 KiB
Python
|
|
# coding:utf-8
|
||
|
|
from enum import Enum
|
||
|
|
import sys
|
||
|
|
from typing import Union
|
||
|
|
|
||
|
|
from PySide6.QtCore import (Qt, QPropertyAnimation, QPoint, QParallelAnimationGroup, QEasingCurve, QMargins,
|
||
|
|
QRectF, QObject, QSize, Signal, QEvent)
|
||
|
|
from PySide6.QtGui import QPixmap, QPainter, QColor, QCursor, QIcon, QImage, QPainterPath, QBrush, QMovie, QImageReader
|
||
|
|
from PySide6.QtWidgets import QWidget, QGraphicsDropShadowEffect, QLabel, QHBoxLayout, QVBoxLayout, QApplication
|
||
|
|
|
||
|
|
from ...common.auto_wrap import TextWrap
|
||
|
|
from ...common.style_sheet import isDarkTheme, FluentStyleSheet
|
||
|
|
from ...common.icon import FluentIconBase, drawIcon, FluentIcon
|
||
|
|
from ...common.screen import getCurrentScreenGeometry
|
||
|
|
from .button import TransparentToolButton
|
||
|
|
from .label import ImageLabel
|
||
|
|
|
||
|
|
|
||
|
|
class FlyoutAnimationType(Enum):
|
||
|
|
""" Flyout animation type """
|
||
|
|
PULL_UP = 0
|
||
|
|
DROP_DOWN = 1
|
||
|
|
SLIDE_LEFT = 2
|
||
|
|
SLIDE_RIGHT = 3
|
||
|
|
FADE_IN = 4
|
||
|
|
NONE = 5
|
||
|
|
|
||
|
|
|
||
|
|
class IconWidget(QWidget):
|
||
|
|
|
||
|
|
def __init__(self, icon, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.setFixedSize(36, 54)
|
||
|
|
self.icon = icon
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
if not self.icon:
|
||
|
|
return
|
||
|
|
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing |
|
||
|
|
QPainter.SmoothPixmapTransform)
|
||
|
|
|
||
|
|
rect = QRectF(8, (self.height()-20)/2, 20, 20)
|
||
|
|
drawIcon(self.icon, painter, rect)
|
||
|
|
|
||
|
|
|
||
|
|
class FlyoutViewBase(QWidget):
|
||
|
|
""" Flyout view base class """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
|
||
|
|
def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
def backgroundColor(self):
|
||
|
|
return QColor(40, 40, 40) if isDarkTheme() else QColor(248, 248, 248)
|
||
|
|
|
||
|
|
def borderColor(self):
|
||
|
|
return QColor(0, 0, 0, 45) if isDarkTheme() else QColor(0, 0, 0, 17)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
|
||
|
|
painter.setBrush(self.backgroundColor())
|
||
|
|
painter.setPen(self.borderColor())
|
||
|
|
|
||
|
|
rect = self.rect().adjusted(1, 1, -1, -1)
|
||
|
|
painter.drawRoundedRect(rect, 8, 8)
|
||
|
|
|
||
|
|
|
||
|
|
class FlyoutView(FlyoutViewBase):
|
||
|
|
""" Flyout view """
|
||
|
|
|
||
|
|
closed = Signal()
|
||
|
|
|
||
|
|
def __init__(self, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
|
||
|
|
image: Union[str, QPixmap, QImage] = None, isClosable=False, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
"""
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
title: str
|
||
|
|
the title of teaching tip
|
||
|
|
|
||
|
|
content: str
|
||
|
|
the content of teaching tip
|
||
|
|
|
||
|
|
icon: InfoBarIcon | FluentIconBase | QIcon | str
|
||
|
|
the icon of teaching tip
|
||
|
|
|
||
|
|
image: str | QPixmap | QImage
|
||
|
|
the image of teaching tip
|
||
|
|
|
||
|
|
isClosable: bool
|
||
|
|
whether to show the close button
|
||
|
|
|
||
|
|
parent: QWidget
|
||
|
|
parent widget
|
||
|
|
"""
|
||
|
|
self.icon = icon
|
||
|
|
self.title = title
|
||
|
|
self.image = image
|
||
|
|
self.content = content
|
||
|
|
self.isClosable = isClosable
|
||
|
|
|
||
|
|
self.vBoxLayout = QVBoxLayout(self)
|
||
|
|
self.viewLayout = QHBoxLayout()
|
||
|
|
self.widgetLayout = QVBoxLayout()
|
||
|
|
|
||
|
|
self.titleLabel = QLabel(title, self)
|
||
|
|
self.contentLabel = QLabel(content, self)
|
||
|
|
self.iconWidget = IconWidget(icon, self)
|
||
|
|
self.imageLabel = ImageLabel(self)
|
||
|
|
self.closeButton = TransparentToolButton(FluentIcon.CLOSE, self)
|
||
|
|
|
||
|
|
self.__initWidgets()
|
||
|
|
|
||
|
|
def __initWidgets(self):
|
||
|
|
self.imageLabel.setImage(self.image)
|
||
|
|
|
||
|
|
self.closeButton.setFixedSize(32, 32)
|
||
|
|
self.closeButton.setIconSize(QSize(12, 12))
|
||
|
|
self.closeButton.setVisible(self.isClosable)
|
||
|
|
self.titleLabel.setVisible(bool(self.title))
|
||
|
|
self.contentLabel.setVisible(bool(self.content))
|
||
|
|
self.iconWidget.setHidden(self.icon is None)
|
||
|
|
|
||
|
|
self.closeButton.clicked.connect(self.closed)
|
||
|
|
|
||
|
|
self.titleLabel.setObjectName('titleLabel')
|
||
|
|
self.contentLabel.setObjectName('contentLabel')
|
||
|
|
FluentStyleSheet.TEACHING_TIP.apply(self)
|
||
|
|
|
||
|
|
self.__initLayout()
|
||
|
|
|
||
|
|
def __initLayout(self):
|
||
|
|
self.vBoxLayout.setContentsMargins(1, 1, 1, 1)
|
||
|
|
self.widgetLayout.setContentsMargins(0, 8, 0, 8)
|
||
|
|
self.viewLayout.setSpacing(4)
|
||
|
|
self.widgetLayout.setSpacing(0)
|
||
|
|
self.vBoxLayout.setSpacing(0)
|
||
|
|
|
||
|
|
# add icon widget
|
||
|
|
if not self.title or not self.content:
|
||
|
|
self.iconWidget.setFixedHeight(36)
|
||
|
|
|
||
|
|
self.vBoxLayout.addLayout(self.viewLayout)
|
||
|
|
self.viewLayout.addWidget(self.iconWidget, 0, Qt.AlignTop)
|
||
|
|
|
||
|
|
# add text
|
||
|
|
self._adjustText()
|
||
|
|
self.widgetLayout.addWidget(self.titleLabel)
|
||
|
|
self.widgetLayout.addWidget(self.contentLabel)
|
||
|
|
self.viewLayout.addLayout(self.widgetLayout)
|
||
|
|
|
||
|
|
# add close button
|
||
|
|
self.closeButton.setVisible(self.isClosable)
|
||
|
|
self.viewLayout.addWidget(
|
||
|
|
self.closeButton, 0, Qt.AlignRight | Qt.AlignTop)
|
||
|
|
|
||
|
|
# adjust content margins
|
||
|
|
margins = QMargins(6, 5, 6, 5)
|
||
|
|
margins.setLeft(20 if not self.icon else 5)
|
||
|
|
margins.setRight(20 if not self.isClosable else 6)
|
||
|
|
self.viewLayout.setContentsMargins(margins)
|
||
|
|
|
||
|
|
# add image
|
||
|
|
self._adjustImage()
|
||
|
|
self._addImageToLayout()
|
||
|
|
|
||
|
|
def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
|
||
|
|
""" add widget to view """
|
||
|
|
self.widgetLayout.addSpacing(8)
|
||
|
|
self.widgetLayout.addWidget(widget, stretch, align)
|
||
|
|
|
||
|
|
def _addImageToLayout(self):
|
||
|
|
self.imageLabel.setBorderRadius(8, 8, 0, 0)
|
||
|
|
self.imageLabel.setHidden(self.imageLabel.isNull())
|
||
|
|
self.vBoxLayout.insertWidget(0, self.imageLabel)
|
||
|
|
|
||
|
|
def _adjustText(self):
|
||
|
|
w = min(900, QApplication.screenAt(
|
||
|
|
QCursor.pos()).geometry().width() - 200)
|
||
|
|
|
||
|
|
# adjust title
|
||
|
|
chars = max(min(w / 10, 120), 30)
|
||
|
|
self.titleLabel.setText(TextWrap.wrap(self.title, chars, False)[0])
|
||
|
|
|
||
|
|
# adjust content
|
||
|
|
chars = max(min(w / 9, 120), 30)
|
||
|
|
self.contentLabel.setText(TextWrap.wrap(self.content, chars, False)[0])
|
||
|
|
|
||
|
|
def _adjustImage(self):
|
||
|
|
w = self.vBoxLayout.sizeHint().width() - 2
|
||
|
|
self.imageLabel.scaledToWidth(w)
|
||
|
|
|
||
|
|
def showEvent(self, e):
|
||
|
|
super().showEvent(e)
|
||
|
|
self._adjustImage()
|
||
|
|
self.adjustSize()
|
||
|
|
|
||
|
|
|
||
|
|
class Flyout(QWidget):
|
||
|
|
""" Flyout """
|
||
|
|
|
||
|
|
closed = Signal()
|
||
|
|
|
||
|
|
def __init__(self, view: FlyoutViewBase, parent=None, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.view = view
|
||
|
|
self.hBoxLayout = QHBoxLayout(self)
|
||
|
|
self.aniManager = None # type: FlyoutAnimationManager
|
||
|
|
self.isDeleteOnClose = isDeleteOnClose
|
||
|
|
self.isMacInputMethodEnabled = isMacInputMethodEnabled
|
||
|
|
|
||
|
|
self.hBoxLayout.setContentsMargins(15, 8, 15, 20)
|
||
|
|
self.hBoxLayout.addWidget(self.view)
|
||
|
|
self.setShadowEffect()
|
||
|
|
|
||
|
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
||
|
|
|
||
|
|
if sys.platform != "darwin" or not isMacInputMethodEnabled:
|
||
|
|
self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint |
|
||
|
|
Qt.NoDropShadowWindowHint)
|
||
|
|
else:
|
||
|
|
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
|
||
|
|
QApplication.instance().installEventFilter(self)
|
||
|
|
|
||
|
|
def eventFilter(self, watched, event):
|
||
|
|
if sys.platform == "darwin" and self.isMacInputMethodEnabled:
|
||
|
|
if self.isVisible() and event.type() == QEvent.MouseButtonPress:
|
||
|
|
if not self.rect().contains(self.mapFromGlobal(event.globalPos())):
|
||
|
|
self.close()
|
||
|
|
|
||
|
|
return super().eventFilter(watched, event)
|
||
|
|
|
||
|
|
def setShadowEffect(self, blurRadius=35, offset=(0, 8)):
|
||
|
|
""" add shadow to dialog """
|
||
|
|
color = QColor(0, 0, 0, 80 if isDarkTheme() else 30)
|
||
|
|
self.shadowEffect = QGraphicsDropShadowEffect(self.view)
|
||
|
|
self.shadowEffect.setBlurRadius(blurRadius)
|
||
|
|
self.shadowEffect.setOffset(*offset)
|
||
|
|
self.shadowEffect.setColor(color)
|
||
|
|
self.view.setGraphicsEffect(None)
|
||
|
|
self.view.setGraphicsEffect(self.shadowEffect)
|
||
|
|
|
||
|
|
def closeEvent(self, e):
|
||
|
|
if self.isDeleteOnClose:
|
||
|
|
self.deleteLater()
|
||
|
|
|
||
|
|
super().closeEvent(e)
|
||
|
|
self.closed.emit()
|
||
|
|
|
||
|
|
def showEvent(self, e):
|
||
|
|
# fixes #780
|
||
|
|
self.activateWindow()
|
||
|
|
super().showEvent(e)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint, aniType=FlyoutAnimationType.PULL_UP):
|
||
|
|
""" show calendar view """
|
||
|
|
self.aniManager = FlyoutAnimationManager.make(aniType, self)
|
||
|
|
self.show()
|
||
|
|
self.aniManager.exec(pos)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def make(cls, view: FlyoutViewBase, target: Union[QWidget, QPoint] = None, parent=None,
|
||
|
|
aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
||
|
|
""" create and show a flyout
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
view: FlyoutViewBase
|
||
|
|
flyout view
|
||
|
|
|
||
|
|
target: QWidget | QPoint
|
||
|
|
the target widget or position to show flyout
|
||
|
|
|
||
|
|
parent: QWidget
|
||
|
|
parent window
|
||
|
|
|
||
|
|
aniType: FlyoutAnimationType
|
||
|
|
flyout animation type
|
||
|
|
|
||
|
|
isDeleteOnClose: bool
|
||
|
|
whether delete flyout automatically when flyout is closed
|
||
|
|
"""
|
||
|
|
w = cls(view, parent, isDeleteOnClose, isMacInputMethodEnabled)
|
||
|
|
|
||
|
|
if target is None:
|
||
|
|
return w
|
||
|
|
|
||
|
|
# show flyout first so that we can get the correct size
|
||
|
|
w.show()
|
||
|
|
|
||
|
|
# move flyout to the top of target
|
||
|
|
if isinstance(target, QWidget):
|
||
|
|
target = FlyoutAnimationManager.make(aniType, w).position(target)
|
||
|
|
|
||
|
|
w.exec(target, aniType)
|
||
|
|
return w
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def create(cls, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
|
||
|
|
image: Union[str, QPixmap, QImage] = None, isClosable=False, target: Union[QWidget, QPoint] = None,
|
||
|
|
parent=None, aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
||
|
|
""" create and show a flyout using the default view
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
title: str
|
||
|
|
the title of teaching tip
|
||
|
|
|
||
|
|
content: str
|
||
|
|
the content of teaching tip
|
||
|
|
|
||
|
|
icon: InfoBarIcon | FluentIconBase | QIcon | str
|
||
|
|
the icon of teaching tip
|
||
|
|
|
||
|
|
image: str | QPixmap | QImage
|
||
|
|
the image of teaching tip
|
||
|
|
|
||
|
|
isClosable: bool
|
||
|
|
whether to show the close button
|
||
|
|
|
||
|
|
target: QWidget | QPoint
|
||
|
|
the target widget or position to show flyout
|
||
|
|
|
||
|
|
parent: QWidget
|
||
|
|
parent window
|
||
|
|
|
||
|
|
aniType: FlyoutAnimationType
|
||
|
|
flyout animation type
|
||
|
|
|
||
|
|
isDeleteOnClose: bool
|
||
|
|
whether delete flyout automatically when flyout is closed
|
||
|
|
"""
|
||
|
|
view = FlyoutView(title, content, icon, image, isClosable)
|
||
|
|
w = cls.make(view, target, parent, aniType, isDeleteOnClose, isMacInputMethodEnabled)
|
||
|
|
view.closed.connect(w.close)
|
||
|
|
return w
|
||
|
|
|
||
|
|
def fadeOut(self):
|
||
|
|
self.fadeOutAni = QPropertyAnimation(self, b'windowOpacity', self)
|
||
|
|
self.fadeOutAni.finished.connect(self.close)
|
||
|
|
self.fadeOutAni.setStartValue(1)
|
||
|
|
self.fadeOutAni.setEndValue(0)
|
||
|
|
self.fadeOutAni.setDuration(120)
|
||
|
|
self.fadeOutAni.start()
|
||
|
|
|
||
|
|
|
||
|
|
class FlyoutAnimationManager(QObject):
|
||
|
|
""" Flyout animation manager """
|
||
|
|
|
||
|
|
managers = {}
|
||
|
|
|
||
|
|
def __init__(self, flyout: Flyout):
|
||
|
|
super().__init__()
|
||
|
|
self.flyout = flyout
|
||
|
|
self.aniGroup = QParallelAnimationGroup(self)
|
||
|
|
self.slideAni = QPropertyAnimation(flyout, b'pos', self)
|
||
|
|
self.opacityAni = QPropertyAnimation(flyout, b'windowOpacity', self)
|
||
|
|
|
||
|
|
self.slideAni.setDuration(187)
|
||
|
|
self.opacityAni.setDuration(187)
|
||
|
|
|
||
|
|
self.opacityAni.setStartValue(0)
|
||
|
|
self.opacityAni.setEndValue(1)
|
||
|
|
|
||
|
|
self.slideAni.setEasingCurve(QEasingCurve.OutQuad)
|
||
|
|
self.opacityAni.setEasingCurve(QEasingCurve.OutQuad)
|
||
|
|
self.aniGroup.addAnimation(self.slideAni)
|
||
|
|
self.aniGroup.addAnimation(self.opacityAni)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def register(cls, name):
|
||
|
|
""" register menu animation manager
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
name: Any
|
||
|
|
the name of manager, it should be unique
|
||
|
|
"""
|
||
|
|
def wrapper(Manager):
|
||
|
|
if name not in cls.managers:
|
||
|
|
cls.managers[name] = Manager
|
||
|
|
|
||
|
|
return Manager
|
||
|
|
|
||
|
|
return wrapper
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
""" start animation """
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
def _adjustPosition(self, pos):
|
||
|
|
rect = getCurrentScreenGeometry()
|
||
|
|
w, h = self.flyout.sizeHint().width() + 5, self.flyout.sizeHint().height()
|
||
|
|
x = max(rect.left(), min(pos.x(), rect.right() - w))
|
||
|
|
y = max(rect.top(), min(pos.y() - 4, rect.bottom() - h + 5))
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
""" return the top left position relative to the target """
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def make(cls, aniType: FlyoutAnimationType, flyout: Flyout) -> "FlyoutAnimationManager":
|
||
|
|
""" mask animation manager """
|
||
|
|
if aniType not in cls.managers:
|
||
|
|
raise ValueError(f'`{aniType}` is an invalid animation type.')
|
||
|
|
|
||
|
|
return cls.managers[aniType](flyout)
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.PULL_UP)
|
||
|
|
class PullUpFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Pull up flyout animation manager """
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
w = self.flyout
|
||
|
|
pos = target.mapToGlobal(QPoint())
|
||
|
|
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
||
|
|
y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
pos = self._adjustPosition(pos)
|
||
|
|
self.slideAni.setStartValue(pos+QPoint(0, 8))
|
||
|
|
self.slideAni.setEndValue(pos)
|
||
|
|
self.aniGroup.start()
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.DROP_DOWN)
|
||
|
|
class DropDownFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Drop down flyout animation manager """
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
w = self.flyout
|
||
|
|
pos = target.mapToGlobal(QPoint(0, target.height()))
|
||
|
|
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
||
|
|
y = pos.y() - w.layout().contentsMargins().top() + 8
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
pos = self._adjustPosition(pos)
|
||
|
|
self.slideAni.setStartValue(pos-QPoint(0, 8))
|
||
|
|
self.slideAni.setEndValue(pos)
|
||
|
|
self.aniGroup.start()
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_LEFT)
|
||
|
|
class SlideLeftFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Slide left flyout animation manager """
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
w = self.flyout
|
||
|
|
pos = target.mapToGlobal(QPoint(0, 0))
|
||
|
|
x = pos.x() - w.sizeHint().width() + 8
|
||
|
|
y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
|
||
|
|
w.layout().contentsMargins().top()
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
pos = self._adjustPosition(pos)
|
||
|
|
self.slideAni.setStartValue(pos+QPoint(8, 0))
|
||
|
|
self.slideAni.setEndValue(pos)
|
||
|
|
self.aniGroup.start()
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_RIGHT)
|
||
|
|
class SlideRightFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Slide right flyout animation manager """
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
w = self.flyout
|
||
|
|
pos = target.mapToGlobal(QPoint(0, 0))
|
||
|
|
x = pos.x() + target.width() - 8
|
||
|
|
y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
|
||
|
|
w.layout().contentsMargins().top()
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
pos = self._adjustPosition(pos)
|
||
|
|
self.slideAni.setStartValue(pos-QPoint(8, 0))
|
||
|
|
self.slideAni.setEndValue(pos)
|
||
|
|
self.aniGroup.start()
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.FADE_IN)
|
||
|
|
class FadeInFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Fade in flyout animation manager """
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
w = self.flyout
|
||
|
|
pos = target.mapToGlobal(QPoint())
|
||
|
|
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
||
|
|
y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
|
||
|
|
return QPoint(x, y)
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
self.flyout.move(self._adjustPosition(pos))
|
||
|
|
self.aniGroup.removeAnimation(self.slideAni)
|
||
|
|
self.aniGroup.start()
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
@FlyoutAnimationManager.register(FlyoutAnimationType.NONE)
|
||
|
|
class DummyFlyoutAnimationManager(FlyoutAnimationManager):
|
||
|
|
""" Dummy flyout animation manager """
|
||
|
|
|
||
|
|
def exec(self, pos: QPoint):
|
||
|
|
""" start animation """
|
||
|
|
self.flyout.move(self._adjustPosition(pos))
|
||
|
|
|
||
|
|
def position(self, target: QWidget):
|
||
|
|
""" return the top left position relative to the target """
|
||
|
|
m = self.flyout.hBoxLayout.contentsMargins()
|
||
|
|
return target.mapToGlobal(QPoint(-m.left(), -self.flyout.sizeHint().height()+m.bottom()-8))
|