132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
# -*- 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)
|