211 lines
7.1 KiB
Python
211 lines
7.1 KiB
Python
"""
|
|
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()
|