# -*- coding: utf-8 -*- """ 토글 스위치 모듈 iOS 스타일의 토글 스위치 위젯을 정의합니다. """ from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel from PySide6.QtCore import Qt, Signal, Property, QPropertyAnimation, QEasingCurve, QRectF from PySide6.QtGui import QPainter, QColor, QBrush, QPen, QFont from core.config import ConfigManager from core.logger import get_logger logger = get_logger(__name__) class ToggleSwitch(QWidget): """ 토글 스위치 위젯 iOS 스타일의 슬라이딩 토글 스위치입니다. 부드러운 애니메이션 효과를 제공합니다. Signals: toggled: 토글 상태 변경 시그널 (bool) Examples: >>> switch = ToggleSwitch(initial_state=True) >>> switch.toggled.connect(self.on_toggle) """ toggled = Signal(bool) def __init__( self, parent=None, initial_state: bool = False, width: int = 50, height: int = 26, on_color: str = "#22c55e", off_color: str = "#64748b" ): super().__init__(parent) self.config = ConfigManager() self._is_on = initial_state self._width = width self._height = height self._on_color = on_color self._off_color = off_color # 핸들 위치 (애니메이션용) self._handle_position = width - height + 4 if initial_state else 4 # 크기 설정 self.setFixedSize(width, height) self.setCursor(Qt.PointingHandCursor) # 애니메이션 self._animation = QPropertyAnimation(self, b"handle_position") self._animation.setDuration(150) self._animation.setEasingCurve(QEasingCurve.InOutQuad) def get_handle_position(self) -> float: """핸들 위치 반환""" return self._handle_position def set_handle_position(self, value: float): """핸들 위치 설정""" self._handle_position = value self.update() handle_position = Property(float, get_handle_position, set_handle_position) def paintEvent(self, event): """페인트 이벤트""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 배경 (라운드 렉트) if self._is_on: bg_color = QColor(self._on_color) else: bg_color = QColor(self._off_color) painter.setBrush(QBrush(bg_color)) painter.setPen(Qt.NoPen) painter.drawRoundedRect( 0, 0, self._width, self._height, self._height // 2, self._height // 2 ) # 핸들 (원) handle_size = self._height - 8 painter.setBrush(QBrush(QColor("#ffffff"))) painter.drawEllipse( int(self._handle_position), 4, handle_size, handle_size ) def mousePressEvent(self, event): """마우스 클릭 이벤트""" if event.button() == Qt.LeftButton: self.toggle() def toggle(self): """토글 상태 전환""" self._is_on = not self._is_on # 애니메이션 실행 end_position = self._width - self._height + 4 if self._is_on else 4 self._animation.setStartValue(self._handle_position) self._animation.setEndValue(end_position) self._animation.start() self.toggled.emit(self._is_on) @property def is_on(self) -> bool: """현재 상태 반환""" return self._is_on def set_state(self, state: bool, animate: bool = True): """ 상태 설정 Args: state: 새로운 상태 animate: 애니메이션 여부 """ if state == self._is_on: return self._is_on = state end_position = self._width - self._height + 4 if state else 4 if animate: self._animation.setStartValue(self._handle_position) self._animation.setEndValue(end_position) self._animation.start() else: self._handle_position = end_position self.update() class LabeledToggle(QWidget): """ 라벨이 있는 토글 스위치 라벨과 토글 스위치를 함께 제공합니다. """ toggled = Signal(bool) def __init__( self, label: str, parent=None, initial_state: bool = False, label_position: str = "left" ): super().__init__(parent) self.config = ConfigManager() self._setup_ui(label, initial_state, label_position) def _setup_ui(self, label: str, initial_state: bool, label_position: str): """UI 설정""" layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(12) # 라벨 self.label = QLabel(label) self.label.setFont(QFont("GmarketSans", 13)) theme = self.config.theme text_color = "#f8fafc" if theme == 'dark' else "#1e293b" self.label.setStyleSheet(f"color: {text_color};") # 토글 스위치 self.switch = ToggleSwitch(initial_state=initial_state) self.switch.toggled.connect(self.toggled.emit) # 레이아웃 if label_position == "left": layout.addWidget(self.label) layout.addStretch() layout.addWidget(self.switch) else: layout.addWidget(self.switch) layout.addWidget(self.label) layout.addStretch() @property def is_on(self) -> bool: """현재 상태 반환""" return self.switch.is_on def set_state(self, state: bool): """상태 설정""" self.switch.set_state(state)