# -*- coding: utf-8 -*- # gui/components/kanban_column.py """Переиспользуемая колонка для канбан-доски.""" from typing import List, Optional from PySide6.QtCore import Signal from PySide6.QtWidgets import QWidget from gui.components.kanban_card import KanbanCard from gui.components.label import Label from gui.components.springs import VSpring from gui.containers.h_container import HContainer from gui.containers.s_container import SContainer from gui.containers.scroll_container import ScrollContainer from gui.containers.v_container import VContainer class KanbanColumn(SContainer): """Вертикальная колонка канбан-доски с заголовком и карточками.""" card_clicked = Signal(object) def __init__( self, column_id: object, title: str = "", color: str = "#DFE1E6", style: str = "KANBAN_COLUMN", parent: Optional[QWidget] = None, ): super().__init__( width_percent=20, margin=[4, 0, 4, 0], spacing=0, style=style, content_fit=True, parent=parent, ) self._column_id = column_id self._color = color self._cards: List[KanbanCard] = [] self._build_ui(title, color) @property def column_id(self) -> object: """Идентификатор колонки.""" return self._column_id @property def card_count(self) -> int: """Количество карточек в колонке.""" return len(self._cards) def set_title(self, text: str) -> None: """Установить заголовок колонки.""" self._title_label.set_text(text) def update_counter(self) -> None: """Обновить текст счётчика.""" self._counter_label.set_text(str(len(self._cards))) def add_card(self, card: KanbanCard) -> None: """Добавить карточку в колонку перед нижней пружиной.""" card.card_clicked.connect(self.card_clicked.emit) self._card_container.insert_widget(len(self._cards), card) self._cards.append(card) self.update_counter() def remove_card(self, card_id: object) -> Optional[KanbanCard]: """Удалить карточку по ID. Возвращает удалённую карточку или None.""" for card in self._cards: if card.card_id == card_id: self._cards.remove(card) self._detach_card(card) self.update_counter() return card return None def clear_cards(self) -> None: """Удалить все карточки из колонки.""" for card in list(self._cards): self._detach_card(card) self._cards.clear() self.update_counter() def get_card(self, card_id: object) -> Optional[KanbanCard]: """Получить карточку по ID.""" for card in self._cards: if card.card_id == card_id: return card return None def _build_ui(self, title: str, color: str) -> None: header = HContainer( height_percent=6, margin=[8, 6, 8, 2], parent=self, ) color_strip = QWidget() color_strip.setFixedWidth(4) color_strip.setFixedHeight(16) color_strip.setStyleSheet( f"background-color: {color}; border: none; border-radius: 2px;" ) header.add_widget(color_strip) self._title_label = Label(title, style="KANBAN_COLUMN_HEADER") header.add_widget(self._title_label) header.add_stretch() self._counter_label = Label("0", style="KANBAN_COUNTER") self._counter_label.set_fixed_size(24, 24) header.add_widget(self._counter_label) self._scroll = ScrollContainer( orientation="v", spacing=0, content_margins=[0, 2, 0, 2], vertical_scroll_bar_policy="as_needed", horizontal_scroll_bar_policy="always_off", parent=self, ) self._card_container = VContainer(spacing=2, parent=self._scroll) self._card_container.add_widget(VSpring()) def _detach_card(self, card: KanbanCard) -> None: self._card_container.remove_widget(card) card.setParent(None)