handOver2/ui/components/toggle_switch.py

208 lines
5.8 KiB
Python

# -*- 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)