from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel from PyQt6.QtGui import QResizeEvent, QCloseEvent from PyQt6.QtCore import QSize, QEvent from app.core.main_manager import MainManager, NotificationType from app.ui.widgets.tabs_widget import TabsWidget, MenuDirection, ButtonPosition, BorderSide, TabSide from app.ui.windows.settings_window import SettingsWindow from app.ui.windows.suggestion_window import SuggestionWindow from app.ui.windows.activation_window import ActivationWindow import app.utils.paths as paths, shutil from typing import Optional class MainWindow(QMainWindow): def __init__(self) -> None: super().__init__() self.main_manager: MainManager = MainManager.get_instance() self.language_manager = self.main_manager.get_language_manager() self.theme_manager = self.main_manager.get_theme_manager() self.settings_manager = self.main_manager.get_settings_manager() self.observer_manager = self.main_manager.get_observer_manager() self.observer_manager.subscribe(NotificationType.THEME, self.update_theme) self.is_maximizing: bool = False # Initialiser les attributs de taille AVANT setup_ui app: Optional[QApplication] = QApplication.instance() size: QSize = app.primaryScreen().size() self.settings_manager.minScreenSize = min(size.height(), size.width()) # Initialiser les tailles par défaut window_size: dict = self.settings_manager.get_window_size() self.current_size: QSize = QSize(window_size["width"], window_size["height"]) self.previous_size: QSize = QSize(window_size["width"], window_size["height"]) # Configuration des tailles de police de référence self.base_width = 600 # Largeur de référence self.base_height = 450 # Hauteur de référence # Cache pour stocker les font-sizes de base de chaque widget self._base_font_sizes = {} # UI elements self.side_menu: TabsWidget self.settings_window: SettingsWindow self.suggestion_window: SuggestionWindow self.setMinimumSize(600, 450) # Initialiser l'UI immédiatement (sera fait pendant le splash) self.setup_ui() # Différer l'application des paramètres de fenêtre jusqu'à l'affichage réel self._window_state_applied = False def showEvent(self, event): """Applique les paramètres de fenêtre lors du premier affichage""" super().showEvent(event) if not self._window_state_applied: self.apply_saved_window_state() self._window_state_applied = True def apply_saved_window_state(self) -> None: """Apply saved window size and maximized state""" window_size: dict = self.settings_manager.get_window_size() self.current_size = QSize(window_size["width"], window_size["height"]) self.previous_size = QSize(window_size["width"], window_size["height"]) self.resize(self.current_size) if self.settings_manager.get_maximized(): self.is_maximizing = True self.showMaximized() def changeEvent(self, event: QEvent) -> None: """Handle window state changes""" super().changeEvent(event) if event.type() == event.Type.WindowStateChange: if self.isMaximized(): # On vient de maximiser self.is_maximizing = False else: # On vient de dé-maximiser, restaurer la taille précédente if hasattr(self, 'previous_size'): self.current_size = self.previous_size self.settings_manager.set_maximized(self.isMaximized()) def resizeEvent(self, a0: QResizeEvent) -> None: # Ne pas sauvegarder la taille si on est en train de maximiser if not self.isMaximized() and not self.is_maximizing: self.previous_size = self.current_size self.current_size = self.size() # Ajuster dynamiquement les font-sizes self.adjust_all_font_sizes() def adjust_all_font_sizes(self): """Ajuste dynamiquement les font-sizes de tous les labels dans toutes les tabs""" # Calculer le ratio basé sur la largeur ET la hauteur actuelle current_width = self.width() current_height = self.height() # Calculer les ratios séparément width_ratio = current_width / self.base_width height_ratio = current_height / self.base_height # Prendre le ratio le plus petit pour éviter que le texte dépasse ratio = min(width_ratio, height_ratio) # Récupérer tous les widgets des tabs all_widgets = [] if hasattr(self, 'side_menu'): all_widgets = self.side_menu.widgets # Parcourir tous les widgets et ajuster leurs labels for widget in all_widgets: if widget: self._adjust_widget_labels(widget, ratio) def _adjust_widget_labels(self, widget, ratio): """Ajuste récursivement tous les QLabel, QPushButton, QLineEdit, QTextEdit et QComboBox d'un widget""" from PyQt6.QtWidgets import QPushButton, QLineEdit, QTextEdit, QComboBox # Types de widgets à ajuster widget_types = [QLabel, QPushButton, QLineEdit, QTextEdit, QComboBox] font_size_dict = self.extract_base_font_size() for widget_type in widget_types: for child in widget.findChildren(widget_type): # Obtenir l'identifiant unique du widget widget_id = id(child) # Si c'est la première fois qu'on voit ce widget, extraire sa font-size de base if widget_id not in self._base_font_sizes: base_size = font_size_dict.get(child.__class__.__name__, 14) self._base_font_sizes[widget_id] = base_size else: base_size = self._base_font_sizes[widget_id] # Calculer la nouvelle taille new_size = int(base_size * ratio) # Appliquer le style (en préservant les autres styles existants) current_style = child.styleSheet() # Retirer l'ancienne font-size si elle existe style_parts = [s.strip() for s in current_style.split(';') if s.strip()] style_parts = [s for s in style_parts if not s.startswith('font-size')] # Ajouter la nouvelle font-size style_parts.append(f'font-size: {new_size}px') new_style = '; '.join(style_parts) child.setStyleSheet(new_style) def extract_base_font_size(self) -> dict: """Extrait la font-size de base d'un widget depuis son stylesheet""" base_font_sizes = {} try: style = self.theme_manager.get_sheet() # Chercher "font-size: XXpx" dans le style, puis chercher à quel widget cela correspond lines = style.splitlines() component = None for line in lines: line = line.strip() if line.startswith("font-size:"): size_part = line.split(":")[1].strip().rstrip(";") if size_part.endswith("px"): size_value = int(size_part[:-2]) base_font_sizes[component] = size_value elif line.startswith("Q"): component = line.split("{")[0].strip() return base_font_sizes except Exception: # En cas d'erreur, retourner une valeur par défaut return {} def closeEvent(self, event: QCloseEvent) -> None: """Handle application close event""" super().closeEvent(event) # si la difference de taille est plus grande que 10 pixels, enregistrer previoussize if abs(self.current_size.width() - self.previous_size.width()) > 10 or abs(self.current_size.height() - self.previous_size.height()) > 10: self.current_size = self.previous_size self.settings_manager.set_window_size( self.current_size.width(), self.current_size.height() ) try: shutil.rmtree(paths.get_user_temp(self.settings_manager.get_config("app_name"))) except Exception: pass def setup_ui(self) -> None: self.side_menu = TabsWidget(self, MenuDirection.HORIZONTAL, 70, None, 10, 1, BorderSide.BOTTOM, TabSide.TOP) self.suggestion_window = SuggestionWindow(self) self.side_menu.add_widget(self.suggestion_window, "", paths.get_asset_svg_path("suggestion"), position=ButtonPosition.CENTER) self.settings_window = SettingsWindow(self) self.side_menu.add_widget(self.settings_window, "", paths.get_asset_svg_path("settings"), position=ButtonPosition.CENTER) # Ajouter la tab d'activation uniquement si le système de licence est activé if self.settings_manager.get_config("enable_licensing"): self.activation_window = ActivationWindow(self) self.side_menu.add_widget(self.activation_window, "", paths.get_asset_svg_path("license"), position=ButtonPosition.END) self.setCentralWidget(self.side_menu) def update_theme(self) -> None: self.setStyleSheet(self.theme_manager.get_sheet())