182 lines
4.9 KiB
Python
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};
|
|
}}
|
|
""")
|
|
|
|
|
|
|
|
|
|
|