335 lines
12 KiB
Python
335 lines
12 KiB
Python
# app/ui/views/side_menu_view.py
|
||
|
||
from PySide6.QtWidgets import (
|
||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFrame,
|
||
QScrollArea, QSizePolicy, QGraphicsDropShadowEffect
|
||
)
|
||
from PySide6.QtCore import Qt, QSize, QPropertyAnimation, QEasingCurve, Property
|
||
from PySide6.QtGui import QIcon, QColor
|
||
|
||
from app.core.theme import theme_manager
|
||
|
||
class SideMenuButton(QPushButton):
|
||
"""Кастомная кнопка для пунктов бокового меню."""
|
||
def __init__(self, icon_path: str, text: str):
|
||
super().__init__()
|
||
self.setCursor(Qt.PointingHandCursor)
|
||
self.setObjectName("SideMenuButton")
|
||
|
||
layout = QHBoxLayout(self)
|
||
layout.setContentsMargins(10, 8, 10, 8)
|
||
layout.setSpacing(15)
|
||
|
||
icon_label = QLabel(icon_path) # Используем текстовые иконки
|
||
icon_label.setObjectName("SideMenuButtonIcon")
|
||
icon_label.setFixedSize(24, 24)
|
||
icon_label.setAlignment(Qt.AlignCenter)
|
||
|
||
text_label = QLabel(text)
|
||
text_label.setObjectName("SideMenuButtonText")
|
||
text_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||
|
||
layout.addWidget(icon_label)
|
||
layout.addWidget(text_label)
|
||
|
||
class SideMenuFooterButton(QPushButton):
|
||
"""Кастомная кнопка для футера бокового меню."""
|
||
def __init__(self, icon_path: str, text: str):
|
||
super().__init__()
|
||
self.setCursor(Qt.PointingHandCursor)
|
||
self.setObjectName("SideMenuFooterButton")
|
||
|
||
layout = QVBoxLayout(self)
|
||
layout.setContentsMargins(5, 8, 5, 8)
|
||
layout.setSpacing(4)
|
||
|
||
icon_label = QLabel(icon_path)
|
||
icon_label.setObjectName("SideMenuFooterIcon")
|
||
icon_label.setAlignment(Qt.AlignCenter)
|
||
|
||
text_label = QLabel(text)
|
||
text_label.setObjectName("SideMenuFooterText")
|
||
text_label.setAlignment(Qt.AlignCenter)
|
||
|
||
layout.addWidget(icon_label)
|
||
layout.addWidget(text_label)
|
||
|
||
|
||
class SideMenuView(QFrame):
|
||
"""Виджет бокового меню."""
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setObjectName("SideMenuView")
|
||
self.setFixedWidth(280)
|
||
|
||
# --- Основной макет ---
|
||
main_layout = QVBoxLayout(self)
|
||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||
main_layout.setSpacing(0)
|
||
|
||
# --- Компоненты ---
|
||
self.header = self._create_header()
|
||
self.account_list_container = self._create_account_list()
|
||
self.scroll_area = self._create_scroll_area()
|
||
self.footer = self._create_footer()
|
||
|
||
main_layout.addWidget(self.header)
|
||
main_layout.addWidget(self.account_list_container)
|
||
main_layout.addWidget(self.scroll_area, 1) # Фактор растяжения
|
||
main_layout.addWidget(self.footer)
|
||
|
||
# --- Анимация списка аккаунтов ---
|
||
self.account_list_container.setVisible(False)
|
||
self.account_list_animation = QPropertyAnimation(self.account_list_container, b"maximumHeight")
|
||
self.account_list_animation.setDuration(300)
|
||
self.account_list_animation.setEasingCurve(QEasingCurve.InOutQuart)
|
||
|
||
# --- Состояние анимации ---
|
||
self.is_closing_animation = False
|
||
self.account_list_animation.finished.connect(self._on_animation_finished)
|
||
|
||
self.update_styles()
|
||
theme_manager.theme_changed.connect(self.update_styles)
|
||
|
||
def _create_header(self):
|
||
"""Создает заголовок меню."""
|
||
header_widget = QWidget()
|
||
header_layout = QVBoxLayout(header_widget)
|
||
header_layout.setContentsMargins(15, 50, 15, 10)
|
||
header_layout.setSpacing(10)
|
||
|
||
# Верхняя часть: Аватар и кнопка темы
|
||
top_row_layout = QHBoxLayout()
|
||
|
||
avatar_button = QPushButton("👤")
|
||
avatar_button.setObjectName("AvatarButton")
|
||
avatar_button.setFixedSize(60, 60)
|
||
|
||
self.theme_toggle_button = QPushButton("☀️")
|
||
self.theme_toggle_button.setObjectName("ThemeToggleButton")
|
||
self.theme_toggle_button.setCursor(Qt.PointingHandCursor)
|
||
self.theme_toggle_button.clicked.connect(theme_manager.toggle_theme)
|
||
|
||
top_row_layout.addWidget(avatar_button)
|
||
top_row_layout.addStretch()
|
||
top_row_layout.addWidget(self.theme_toggle_button)
|
||
|
||
# Нижняя часть: Имя пользователя и кнопка раскрытия
|
||
self.account_toggle_button = QPushButton()
|
||
self.account_toggle_button.setObjectName("AccountToggleButton")
|
||
self.account_toggle_button.setCursor(Qt.PointingHandCursor)
|
||
self.account_toggle_button.clicked.connect(self.toggle_account_list)
|
||
|
||
bottom_row_layout = QHBoxLayout(self.account_toggle_button)
|
||
|
||
user_info_layout = QVBoxLayout()
|
||
user_info_layout.setSpacing(0)
|
||
user_info_layout.addWidget(QLabel("Your Name"))
|
||
user_info_layout.addWidget(QLabel("@yourusername"))
|
||
|
||
self.expand_icon = QLabel("▼")
|
||
|
||
bottom_row_layout.addLayout(user_info_layout)
|
||
bottom_row_layout.addStretch()
|
||
bottom_row_layout.addWidget(self.expand_icon)
|
||
|
||
header_layout.addLayout(top_row_layout)
|
||
header_layout.addWidget(self.account_toggle_button)
|
||
|
||
return header_widget
|
||
|
||
def _create_account_list(self):
|
||
"""Создает выпадающий список аккаунтов."""
|
||
container = QWidget()
|
||
container.setObjectName("AccountListContainer")
|
||
layout = QVBoxLayout(container)
|
||
layout.setContentsMargins(15, 10, 15, 10)
|
||
layout.setSpacing(15)
|
||
|
||
# Пример аккаунтов
|
||
accounts = [
|
||
("Your Name", "@yourusername", True),
|
||
("Second Account", "@second", False)
|
||
]
|
||
|
||
for name, username, is_current in accounts:
|
||
# Здесь можно создать более сложный виджет для каждого аккаунта
|
||
label = QLabel(f"{name} ({username}) {'✔' if is_current else ''}")
|
||
layout.addWidget(label)
|
||
|
||
return container
|
||
|
||
def _create_scroll_area(self):
|
||
"""Создает прокручиваемую область с пунктами меню."""
|
||
scroll_area = QScrollArea()
|
||
scroll_area.setWidgetResizable(True)
|
||
scroll_area.setFrameShape(QFrame.NoFrame)
|
||
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||
scroll_area.setObjectName("ScrollArea")
|
||
|
||
content_widget = QWidget()
|
||
content_layout = QVBoxLayout(content_widget)
|
||
content_layout.setContentsMargins(15, 10, 15, 10)
|
||
content_layout.setSpacing(5)
|
||
|
||
# Секция 1
|
||
content_layout.addWidget(SideMenuButton("👥", "People You May Like"))
|
||
content_layout.addWidget(SideMenuButton("⭐", "Fun Fest"))
|
||
content_layout.addWidget(SideMenuButton("💡", "Creator Center"))
|
||
|
||
content_layout.addSpacing(10)
|
||
content_layout.addWidget(self._create_divider())
|
||
content_layout.addSpacing(10)
|
||
|
||
# Секция 2
|
||
content_layout.addWidget(self._create_category_label("CATEGORY"))
|
||
content_layout.addWidget(SideMenuButton("📄", "Drafts"))
|
||
content_layout.addWidget(SideMenuButton("💬", "My Comments"))
|
||
|
||
content_layout.addStretch() # Заполняет пространство внизу
|
||
scroll_area.setWidget(content_widget)
|
||
return scroll_area
|
||
|
||
def _create_footer(self):
|
||
"""Создает футер меню."""
|
||
footer_widget = QWidget()
|
||
footer_widget.setObjectName("SideMenuFooter")
|
||
layout = QHBoxLayout(footer_widget)
|
||
layout.setContentsMargins(15, 10, 15, 20)
|
||
|
||
layout.addWidget(SideMenuFooterButton("📱", "Scan"))
|
||
layout.addWidget(SideMenuFooterButton("❓", "Help"))
|
||
layout.addWidget(SideMenuFooterButton("⚙️", "Settings"))
|
||
|
||
return footer_widget
|
||
|
||
def _create_divider(self):
|
||
divider = QFrame()
|
||
divider.setFrameShape(QFrame.HLine)
|
||
divider.setObjectName("Divider")
|
||
return divider
|
||
|
||
def _create_category_label(self, text):
|
||
label = QLabel(text)
|
||
label.setObjectName("CategoryLabel")
|
||
return label
|
||
|
||
def _on_animation_finished(self):
|
||
"""Срабатывает после завершения анимации."""
|
||
if self.is_closing_animation:
|
||
self.account_list_container.setVisible(False)
|
||
|
||
def toggle_account_list(self):
|
||
"""Анимирует показ/скрытие списка аккаунтов."""
|
||
self.account_list_animation.stop()
|
||
|
||
if self.account_list_container.isVisible():
|
||
# --- Закрытие ---
|
||
self.is_closing_animation = True
|
||
self.expand_icon.setText("▼")
|
||
end_height = 0
|
||
else:
|
||
# --- Открытие ---
|
||
self.is_closing_animation = False
|
||
self.expand_icon.setText("▲")
|
||
self.account_list_container.setVisible(True)
|
||
end_height = self.account_list_container.sizeHint().height()
|
||
|
||
self.account_list_animation.setStartValue(self.account_list_container.height())
|
||
self.account_list_animation.setEndValue(end_height)
|
||
self.account_list_animation.start()
|
||
|
||
def update_styles(self):
|
||
"""Обновляет стили в зависимости от темы."""
|
||
is_dark = theme_manager.is_dark()
|
||
self.theme_toggle_button.setText("🌙" if is_dark else "☀️")
|
||
self.setStyleSheet(self.get_stylesheet())
|
||
|
||
def get_stylesheet(self):
|
||
is_dark = theme_manager.is_dark()
|
||
|
||
bg_color = "#1c1c1e" if is_dark else "#ffffff"
|
||
text_color = "#f2f2f7" if is_dark else "#000000"
|
||
secondary_text_color = "#8e8e93" if is_dark else "#888888"
|
||
divider_color = "#3c3c3c" if is_dark else "#e7e7e7"
|
||
button_hover_bg = "#2c2c2e" if is_dark else "#f0f0f0"
|
||
|
||
return f"""
|
||
#SideMenuView {{
|
||
background-color: {bg_color};
|
||
}}
|
||
QPushButton {{
|
||
border: none;
|
||
background: transparent;
|
||
text-align: left;
|
||
color: {text_color};
|
||
}}
|
||
|
||
/* --- Header --- */
|
||
#AvatarButton {{
|
||
font-size: 40px;
|
||
border-radius: 30px;
|
||
background-color: {divider_color};
|
||
}}
|
||
#ThemeToggleButton {{
|
||
font-size: 24px;
|
||
}}
|
||
#AccountToggleButton {{
|
||
padding: 8px;
|
||
border-radius: 8px;
|
||
}}
|
||
#AccountToggleButton:hover {{
|
||
background-color: {button_hover_bg};
|
||
}}
|
||
#AccountToggleButton QLabel {{
|
||
color: {text_color};
|
||
}}
|
||
#AccountToggleButton > QLabel:first-child {{ /* Имя */
|
||
font-weight: bold;
|
||
}}
|
||
#AccountToggleButton > QLabel:last-child {{ /* @username */
|
||
color: {secondary_text_color};
|
||
}}
|
||
|
||
/* --- Account List --- */
|
||
#AccountListContainer {{
|
||
background-color: transparent;
|
||
color: {text_color};
|
||
}}
|
||
|
||
/* --- Menu Buttons --- */
|
||
#SideMenuButton, #SideMenuFooterButton {{
|
||
border-radius: 8px;
|
||
}}
|
||
#SideMenuButton:hover, #SideMenuFooterButton:hover {{
|
||
background-color: {button_hover_bg};
|
||
}}
|
||
#SideMenuButtonIcon, #SideMenuFooterIcon {{
|
||
font-size: 18px;
|
||
color: {text_color};
|
||
}}
|
||
#SideMenuButtonText, #SideMenuFooterText {{
|
||
font-size: 14px;
|
||
color: {text_color};
|
||
}}
|
||
#SideMenuFooterText {{
|
||
font-size: 11px;
|
||
}}
|
||
|
||
/* --- Other --- */
|
||
#Divider {{
|
||
background-color: {divider_color};
|
||
border: none;
|
||
height: 1px;
|
||
}}
|
||
#CategoryLabel {{
|
||
color: {secondary_text_color};
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
padding: 5px 10px;
|
||
}}
|
||
#ScrollArea {{
|
||
border: none;
|
||
}}
|
||
"""
|