handOver2/ui/components/chips/chip_base_button.py

182 lines
4.9 KiB
Python

# -*- coding: utf-8 -*-
"""
공통 칩 버튼 베이스
- 색상/크기/hover/checked(선택) 시각 처리
- 선택형 칩(Choice)과 필터 칩(Filter)의 공통 기반
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
from PySide6.QtWidgets import QToolButton, QSizePolicy
from PySide6.QtCore import Qt, Signal, QSize
from core.logger import get_logger
logger = get_logger(__name__)
try:
from ui.styles.style_manager import StyleManager # type: ignore
except Exception:
StyleManager = None # noqa: N816
@dataclass(frozen=True)
class ChipTheme:
height: int = 28
padding_x: int = 10
radius: int = 14
font_px: int = 12
max_width: Optional[int] = None
class ChipBaseButton(QToolButton):
"""
공통 칩 버튼
- key 기반 신호 제공
- 선택형(checked) 시각 지원
"""
clicked_key = Signal(str)
toggled_key = Signal(str, bool)
def __init__(
self,
text: str,
key: str,
bg: str = "#64748b",
fg: str = "#ffffff",
theme: ChipTheme = ChipTheme(),
checkable: bool = False,
parent=None,
):
super().__init__(parent)
self._style_manager = StyleManager() if StyleManager else None
self.key = key
self._bg = bg
self._fg = fg
self.theme = theme
self.setText(text)
self.setCursor(Qt.PointingHandCursor)
self.setAutoRaise(True)
self.setFocusPolicy(Qt.NoFocus)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setFixedHeight(self.theme.height)
self.setMinimumWidth(40)
if self.theme.max_width:
self.setMaximumWidth(self.theme.max_width)
self.setProperty("chipHover", False)
# 선택형 지원
self.setCheckable(checkable)
# 신호 연결
self.clicked.connect(self._on_clicked)
self.toggled.connect(self._on_toggled)
self._apply_style()
def sizeHint(self) -> QSize:
hint = super().sizeHint()
h = self.theme.height
return QSize(max(hint.width() + 6, 40), h)
def enterEvent(self, event):
self.setProperty("chipHover", True)
self.style().unpolish(self)
self.style().polish(self)
super().enterEvent(event)
def leaveEvent(self, event):
self.setProperty("chipHover", False)
self.style().unpolish(self)
self.style().polish(self)
super().leaveEvent(event)
def _on_clicked(self):
self.clicked_key.emit(self.key)
def _on_toggled(self, checked: bool):
self.toggled_key.emit(self.key, checked)
# checked 시각 반영을 위해 re-polish
self.style().unpolish(self)
self.style().polish(self)
def set_bg(self, bg: str):
"""배경색 설정"""
self._bg = bg
self._apply_style()
def set_fg(self, fg: str):
"""전경색 설정"""
self._fg = fg
self._apply_style()
def _apply_style(self):
# 폰트(StyleManager가 있으면 dialog.content 기반)
if self._style_manager:
try:
f = self._style_manager.get_font("dialog", "content")
self.setFont(f)
except Exception:
logger.debug("StyleManager 폰트 적용 실패. 시스템 기본 폰트 사용")
h = self.theme.height
r = self.theme.radius
px = self.theme.padding_x
self.setStyleSheet(f"""
QToolButton {{
background: {self._bg};
color: {self._fg};
border: 1px solid rgba(0,0,0,0.12);
border-radius: {r}px;
padding-left: {px}px;
padding-right: {px}px;
min-height: {h}px;
max-height: {h}px;
font-size: {self.theme.font_px}px;
font-weight: 700;
}}
QToolButton[chipHover="true"] {{
border: 1px solid rgba(255,255,255,0.55);
background: qlineargradient(
x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255,255,255,0.15),
stop:1 rgba(255,255,255,0.00)
), {self._bg};
}}
/* 선택됨(checked) 강조 */
QToolButton:checked {{
border: 1px solid rgba(255,255,255,0.80);
background: qlineargradient(
x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255,255,255,0.25),
stop:1 rgba(0,0,0,0.10)
), {self._bg};
}}
QToolButton:pressed {{
background: qlineargradient(
x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0,0,0,0.18),
stop:1 rgba(0,0,0,0.05)
), {self._bg};
}}
""")