# coding:utf-8 from typing import List from PySide6.QtCore import Qt, QRect, QRectF, QSize from PySide6.QtGui import QPainter, QColor, QPainterPath, QAction from PySide6.QtWidgets import QLineEdit, QListWidgetItem, QListWidget from ..widgets.menu import (RoundMenu, MenuAnimationType, MenuAnimationManager, MenuActionListWidget, IndicatorMenuItemDelegate, LineEditMenu, MenuIndicatorType, CheckableMenu) from ..widgets.line_edit import CompleterMenu, LineEdit from ..widgets.acrylic_label import AcrylicBrush from ...common.style_sheet import isDarkTheme class AcrylicMenuActionListWidget(MenuActionListWidget): def __init__(self, parent=None): super().__init__(parent) self.acrylicBrush = AcrylicBrush(self.viewport(), 35) self.setViewportMargins(0, 0, 0, 0) self.setProperty("transparent", True) super().addItem(self.createPlaceholderItem(self._topMargin())) super().addItem(self.createPlaceholderItem(self._bottomMargin())) def _updateAcrylicColor(self): if isDarkTheme(): tintColor = QColor(32, 32, 32, 200) luminosityColor = QColor(0, 0, 0, 0) else: tintColor = QColor(255, 255, 255, 160) luminosityColor = QColor(255, 255, 255, 50) self.acrylicBrush.tintColor = tintColor self.acrylicBrush.luminosityColor = luminosityColor def _topMargin(self): return 6 def _bottomMargin(self): return 6 def setItemHeight(self, height: int): """ set the height of item """ if height == self._itemHeight: return for i in range(1, self.count() - 1): item = self.item(i) if not self.itemWidget(item): item.setSizeHint(QSize(item.sizeHint().width(), height)) self._itemHeight = height self.adjustSize() def addItem(self, item): return super().insertItem(self.count() - 1, item) def createPlaceholderItem(self, height=2): item = QListWidgetItem() item.setSizeHint(QSize(1, height)) item.setFlags(Qt.ItemFlag.NoItemFlags) return item def clipPath(self): path = QPainterPath() path.addRoundedRect(QRectF(self.rect()).adjusted(0, 0, -2.5, -2.5), 8, 8) return path def paintEvent(self, e) -> None: painter = QPainter(self.viewport()) painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.acrylicBrush.clipPath = self.clipPath() self._updateAcrylicColor() self.acrylicBrush.paint() super().paintEvent(e) class AcrylicMenuBase: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def setUpMenu(self, view): self.hBoxLayout.removeWidget(self.view) self.view.deleteLater() self.view = view self.hBoxLayout.addWidget(self.view) self.setShadowEffect() self.view.itemClicked.connect(self._onItemClicked) self.view.itemEntered.connect(self._onItemEntered) def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN): p = MenuAnimationManager.make(self, aniType)._endPosition(pos) self.view.acrylicBrush.grabImage(QRect(p, self.layout().sizeHint())) super().exec(pos, ani, aniType) class AcrylicMenu(AcrylicMenuBase, RoundMenu): """ Acrylic menu """ def __init__(self, title="", parent=None): super().__init__(title, parent) self.setUpMenu(AcrylicMenuActionListWidget(self)) def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN): return super().exec(pos, ani, aniType) class AcrylicCompleterMenuActionListWidget(AcrylicMenuActionListWidget): def clipPath(self): path = QPainterPath() path.setFillRule(Qt.FillRule.WindingFill) path.addRoundedRect(QRectF(self.rect()).adjusted(1, 1, -2.5, -2.5), 8, 8) if self.property("dropDown"): path.addRect(1, 1, 11, 11) path.addRect(self.width() - 12, 1, 11, 11) else: path.addRect(1, self.height() - 11, 11, 11) path.addRect(self.width() - 12, self.height() - 11, 11, 11) return path class AcrylicCompleterMenu(AcrylicMenuBase, CompleterMenu): """ Acrylic completer menu """ def __init__(self, lineEdit: LineEdit): super().__init__(lineEdit) self.setUpMenu(AcrylicCompleterMenuActionListWidget(self)) self.view.setObjectName('completerListWidget') self.view.setItemDelegate(IndicatorMenuItemDelegate()) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setItemHeight(33) def _onItemClicked(self, item): self._hideMenu(False) self._onCompletionItemSelected(item.text(), self.view.row(item)-1) def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN): return super().exec(pos, ani, aniType) def setItems(self, items): """ set completion items """ self.view.clear() self.items = items QListWidget.addItem(self.view, self.view.createPlaceholderItem(self.view._topMargin())) self.view.addItems(items) for i in range(1, self.view.count()): item = self.view.item(i) item.setSizeHint(QSize(1, self.itemHeight)) QListWidget.addItem(self.view, self.view.createPlaceholderItem(self.view._bottomMargin())) class AcrylicLineEditMenu(AcrylicMenuBase, LineEditMenu): """ Acrylic line edit menu """ def __init__(self, parent: QLineEdit): super().__init__(parent) self.setUpMenu(AcrylicMenuActionListWidget(self)) def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN): return super().exec(pos, ani, aniType) class AcrylicCheckableMenu(AcrylicMenuBase, CheckableMenu): """ Checkable menu """ def __init__(self, title="", parent=None, indicatorType=MenuIndicatorType.CHECK): super().__init__(title, parent, indicatorType) self.setUpMenu(AcrylicMenuActionListWidget(self)) self.view.setObjectName('checkableListWidget') def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN): return super().exec(pos, ani, aniType) class AcrylicSystemTrayMenu(AcrylicMenu): """ System tray menu """ def showEvent(self, e): super().showEvent(e) self.adjustPosition() self.view.acrylicBrush.grabImage(QRect(self.pos(), self.layout().sizeHint())) class AcrylicCheckableSystemTrayMenu(AcrylicCheckableMenu): """ Checkable system tray menu """ def showEvent(self, e): super().showEvent(e) self.adjustPosition()