272 lines
7.5 KiB
Python
272 lines
7.5 KiB
Python
# coding:utf-8
|
|
from typing import Dict
|
|
|
|
from PySide6.QtCore import Qt, Signal, QRectF
|
|
from PySide6.QtGui import QPainter, QFont, QColor
|
|
from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout, QSizePolicy
|
|
|
|
from ...common.font import setFont
|
|
from ...common.router import qrouter
|
|
from ...common.style_sheet import themeColor, FluentStyleSheet
|
|
from ...common.color import autoFallbackThemeColor
|
|
from ...common.animation import FluentAnimation, FluentAnimationType, FluentAnimationProperty
|
|
from ..widgets.button import PushButton
|
|
from .navigation_panel import RouteKeyError
|
|
|
|
|
|
class PivotItem(PushButton):
|
|
""" Pivot item """
|
|
|
|
itemClicked = Signal(bool)
|
|
|
|
def _postInit(self):
|
|
self.isSelected = False
|
|
self.setProperty('isSelected', False)
|
|
self.clicked.connect(lambda: self.itemClicked.emit(True))
|
|
self.setAttribute(Qt.WA_LayoutUsesWidgetRect)
|
|
|
|
FluentStyleSheet.PIVOT.apply(self)
|
|
setFont(self, 18)
|
|
|
|
def setSelected(self, isSelected: bool):
|
|
if self.isSelected == isSelected:
|
|
return
|
|
|
|
self.isSelected = isSelected
|
|
self.setProperty('isSelected', isSelected)
|
|
self.setStyle(QApplication.style())
|
|
self.update()
|
|
|
|
|
|
class Pivot(QWidget):
|
|
""" Pivot """
|
|
|
|
currentItemChanged = Signal(str)
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.items = {} # type: Dict[str, PivotItem]
|
|
self._currentRouteKey = None
|
|
|
|
self.lightIndicatorColor = QColor()
|
|
self.darkIndicatorColor = QColor()
|
|
|
|
self.hBoxLayout = QHBoxLayout(self)
|
|
self.slideAni = FluentAnimation.create(
|
|
FluentAnimationType.POINT_TO_POINT, FluentAnimationProperty.SCALE, value=0, parent=self)
|
|
|
|
FluentStyleSheet.PIVOT.apply(self)
|
|
|
|
self.hBoxLayout.setSpacing(0)
|
|
self.hBoxLayout.setAlignment(Qt.AlignLeft)
|
|
self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
|
|
self.hBoxLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize)
|
|
|
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
|
|
def addItem(self, routeKey: str, text: str, onClick=None, icon=None):
|
|
""" add item
|
|
|
|
Parameters
|
|
----------
|
|
routeKey: str
|
|
the unique name of item
|
|
|
|
text: str
|
|
the text of navigation item
|
|
|
|
onClick: callable
|
|
the slot connected to item clicked signal
|
|
|
|
icon: str
|
|
the icon of navigation item
|
|
"""
|
|
return self.insertItem(-1, routeKey, text, onClick, icon)
|
|
|
|
def addWidget(self, routeKey: str, widget: PivotItem, onClick=None):
|
|
""" add widget
|
|
|
|
Parameters
|
|
----------
|
|
routeKey: str
|
|
the unique name of item
|
|
|
|
widget: PivotItem
|
|
navigation widget
|
|
|
|
onClick: callable
|
|
the slot connected to item clicked signal
|
|
"""
|
|
self.insertWidget(-1, routeKey, widget, onClick)
|
|
|
|
def insertItem(self, index: int, routeKey: str, text: str, onClick=None, icon=None):
|
|
""" insert item
|
|
|
|
Parameters
|
|
----------
|
|
index: int
|
|
insert position
|
|
|
|
routeKey: str
|
|
the unique name of item
|
|
|
|
text: str
|
|
the text of navigation item
|
|
|
|
onClick: callable
|
|
the slot connected to item clicked signal
|
|
|
|
icon: str
|
|
the icon of navigation item
|
|
"""
|
|
if routeKey in self.items:
|
|
return
|
|
|
|
item = PivotItem(text, self)
|
|
if icon:
|
|
item.setIcon(icon)
|
|
|
|
self.insertWidget(index, routeKey, item, onClick)
|
|
return item
|
|
|
|
def insertWidget(self, index: int, routeKey: str, widget: PivotItem, onClick=None):
|
|
""" insert item
|
|
|
|
Parameters
|
|
----------
|
|
index: int
|
|
insert position
|
|
|
|
routeKey: str
|
|
the unique name of item
|
|
|
|
widget: PivotItem
|
|
navigation widget
|
|
|
|
onClick: callable
|
|
the slot connected to item clicked signal
|
|
"""
|
|
if routeKey in self.items:
|
|
return
|
|
|
|
widget.setProperty('routeKey', routeKey)
|
|
widget.itemClicked.connect(self._onItemClicked)
|
|
if onClick:
|
|
widget.itemClicked.connect(onClick)
|
|
|
|
self.items[routeKey] = widget
|
|
self.hBoxLayout.insertWidget(index, widget, 1)
|
|
|
|
def removeWidget(self, routeKey: str):
|
|
""" remove widget
|
|
|
|
Parameters
|
|
----------
|
|
routeKey: str
|
|
the unique name of item
|
|
"""
|
|
if routeKey not in self.items:
|
|
return
|
|
|
|
item = self.items.pop(routeKey)
|
|
self.hBoxLayout.removeWidget(item)
|
|
qrouter.remove(routeKey)
|
|
item.deleteLater()
|
|
|
|
if not self.items:
|
|
self._currentRouteKey = None
|
|
|
|
def clear(self):
|
|
""" clear all navigation items """
|
|
for k, w in self.items.items():
|
|
self.hBoxLayout.removeWidget(w)
|
|
qrouter.remove(k)
|
|
w.deleteLater()
|
|
|
|
self.items.clear()
|
|
self._currentRouteKey = None
|
|
|
|
def currentItem(self):
|
|
""" Returns the current selected item """
|
|
if self._currentRouteKey is None:
|
|
return None
|
|
|
|
return self.widget(self._currentRouteKey)
|
|
|
|
def currentRouteKey(self):
|
|
return self._currentRouteKey
|
|
|
|
def setCurrentItem(self, routeKey: str):
|
|
""" set current selected item
|
|
|
|
Parameters
|
|
----------
|
|
routeKey: str
|
|
the unique name of item
|
|
"""
|
|
if routeKey not in self.items or routeKey == self.currentRouteKey():
|
|
return
|
|
|
|
self._currentRouteKey = routeKey
|
|
self.slideAni.startAnimation(self.widget(routeKey).x())
|
|
|
|
for k, item in self.items.items():
|
|
item.setSelected(k == routeKey)
|
|
|
|
self.currentItemChanged.emit(routeKey)
|
|
|
|
def showEvent(self, e):
|
|
super().showEvent(e)
|
|
self._adjustIndicatorPos()
|
|
|
|
def setItemFontSize(self, size: int):
|
|
""" set the pixel font size of items """
|
|
for item in self.items.values():
|
|
font = item.font()
|
|
font.setPixelSize(size)
|
|
item.setFont(font)
|
|
item.adjustSize()
|
|
|
|
def setItemText(self, routeKey: str, text: str):
|
|
""" set the text of item """
|
|
item = self.widget(routeKey)
|
|
item.setText(text)
|
|
|
|
def setIndicatorColor(self, light, dark):
|
|
self.lightIndicatorColor = QColor(light)
|
|
self.darkIndicatorColor = QColor(dark)
|
|
self.update()
|
|
|
|
def _onItemClicked(self):
|
|
item = self.sender() # type: PivotItem
|
|
self.setCurrentItem(item.property('routeKey'))
|
|
|
|
def widget(self, routeKey: str):
|
|
if routeKey not in self.items:
|
|
raise RouteKeyError(f"`{routeKey}` is illegal.")
|
|
|
|
return self.items[routeKey]
|
|
|
|
def resizeEvent(self, e) -> None:
|
|
super().resizeEvent(e)
|
|
self._adjustIndicatorPos()
|
|
|
|
def _adjustIndicatorPos(self):
|
|
item = self.currentItem()
|
|
if item:
|
|
self.slideAni.stop()
|
|
self.slideAni.setValue(item.x())
|
|
|
|
def paintEvent(self, e):
|
|
super().paintEvent(e)
|
|
|
|
if not self.currentItem():
|
|
return
|
|
|
|
painter = QPainter(self)
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
|
painter.setPen(Qt.NoPen)
|
|
painter.setBrush(autoFallbackThemeColor(self.lightIndicatorColor, self.darkIndicatorColor))
|
|
|
|
x = int(self.currentItem().width() / 2 - 8 + self.slideAni.value())
|
|
painter.drawRoundedRect(x, self.height() - 3, 16, 3, 1.5, 1.5) |