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;
 | 
						||
            }}
 | 
						||
        """
 |