""" Modern Components Library. ========================== Contains custom-painted widgets that move beyond the standard 'amateur' Qt look. Implements smooth animations, hardware acceleration, and glassmorphism. """ from PySide6.QtWidgets import ( QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGraphicsDropShadowEffect, QFrame, QAbstractButton ) from PySide6.QtCore import Qt, QPropertyAnimation, QEasingCurve, Property, QRect, QPoint, Signal, Slot from PySide6.QtGui import QPainter, QColor, QBrush, QPen, QLinearGradient, QFont from src.ui.styles import Theme class GlassButton(QPushButton): """A premium button with gradient hover effects and smooth scaling.""" def __init__(self, text, parent=None, accent_color=Theme.ACCENT_CYAN): super().__init__(text, parent) self.accent = QColor(accent_color) self.setCursor(Qt.PointingHandCursor) self.setFixedHeight(40) self._hover_opacity = 0.0 self.setStyleSheet(f""" QPushButton {{ background-color: rgba(255, 255, 255, 0.05); border: 1px solid {Theme.BORDER_SUBTLE}; color: {Theme.TEXT_SECONDARY}; border-radius: 8px; padding: 0 20px; font-size: 13px; font-weight: 600; }} """) # Hover Animation self.anim = QPropertyAnimation(self, b"hover_opacity") self.anim.setDuration(200) self.anim.setStartValue(0.0) self.anim.setEndValue(1.0) self.anim.setEasingCurve(QEasingCurve.OutCubic) @Property(float) def hover_opacity(self): return self._hover_opacity @hover_opacity.setter def hover_opacity(self, value): self._hover_opacity = value self.update() def enterEvent(self, event): self.anim.setDirection(QPropertyAnimation.Forward) self.anim.start() super().enterEvent(event) def leaveEvent(self, event): self.anim.setDirection(QPropertyAnimation.Backward) self.anim.start() super().leaveEvent(event) def paintEvent(self, event): """Custom paint for the glow effect.""" super().paintEvent(event) if self._hover_opacity > 0: painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Subtle Glow Border color = QColor(self.accent) color.setAlphaF(self._hover_opacity * 0.5) painter.setPen(QPen(color, 1.5)) painter.setBrush(Qt.NoBrush) painter.drawRoundedRect(self.rect().adjusted(1,1,-1,-1), 8, 8) # Text Glow color shift self.setStyleSheet(f""" QPushButton {{ background-color: rgba(255, 255, 255, {0.05 + (self._hover_opacity * 0.05)}); border: 1px solid {Theme.BORDER_SUBTLE}; color: white; border-radius: 8px; padding: 0 20px; font-size: 13px; font-weight: 600; }} """) class ModernSwitch(QAbstractButton): """A sleek iOS-style toggle switch.""" def __init__(self, parent=None, active_color=Theme.ACCENT_GREEN): super().__init__(parent) self.setCheckable(True) self.setFixedSize(44, 24) self._thumb_pos = 3.0 self.active_color = QColor(active_color) self.anim = QPropertyAnimation(self, b"thumb_pos") self.anim.setDuration(200) self.anim.setEasingCurve(QEasingCurve.InOutCubic) @Property(float) def thumb_pos(self): return self._thumb_pos @thumb_pos.setter def thumb_pos(self, value): self._thumb_pos = value self.update() def nextCheckState(self): super().nextCheckState() self.anim.stop() if self.isChecked(): self.anim.setEndValue(23.0) else: self.anim.setEndValue(3.0) self.anim.start() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Background bg_color = QColor("#2d2d3d") if self.isChecked(): bg_color = self.active_color painter.setBrush(bg_color) painter.setPen(Qt.NoPen) painter.drawRoundedRect(self.rect(), 12, 12) # Thumb painter.setBrush(Qt.white) painter.drawEllipse(QPoint(self._thumb_pos + 9, 12), 9, 9) class ModernFrame(QFrame): """A base frame with rounded corners and a shadow.""" def __init__(self, parent=None): super().__init__(parent) self.setObjectName("premiumFrame") self.setStyleSheet(f""" #premiumFrame {{ background-color: {Theme.BG_CARD}; border: 1px solid {Theme.BORDER_SUBTLE}; border-radius: 12px; }} """) self.shadow = QGraphicsDropShadowEffect(self) self.shadow.setBlurRadius(25) self.shadow.setXOffset(0) self.shadow.setYOffset(8) self.shadow.setColor(QColor(0, 0, 0, 180)) self.setGraphicsEffect(self.shadow) from PySide6.QtWidgets import ( QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGraphicsDropShadowEffect, QFrame, QAbstractButton, QSlider ) class ModernSlider(QSlider): """A custom painted modern slider with a glowing knob.""" def __init__(self, orientation=Qt.Horizontal, parent=None): super().__init__(orientation, parent) self.setStyleSheet(f""" QSlider::groove:horizontal {{ border: 1px solid {Theme.BG_DARK}; height: 4px; background: {Theme.BG_DARK}; margin: 2px 0; border-radius: 2px; }} QSlider::handle:horizontal {{ background: {Theme.ACCENT_CYAN}; border: 2px solid white; width: 16px; height: 16px; margin: -7px 0; border-radius: 8px; }} QSlider::add-page:horizontal {{ background: {Theme.BG_DARK}; }} QSlider::sub-page:horizontal {{ background: {Theme.ACCENT_CYAN}; border-radius: 2px; }} """) class FramelessWindow(QWidget): """Base class for all premium windows to handle dragging and frameless logic.""" def __init__(self, parent=None): super().__init__(parent) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.NoDropShadowWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self._drag_pos = None def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.move(event.globalPosition().toPoint() - self._drag_pos) event.accept()