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, TextPosition 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.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language) 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 = 1200 # Largeur de référence (taille par défaut) self.base_height = 700 # Hauteur de référence (taille par défaut) self.base_tab_height = 70 # Hauteur de base du tab menu # Cache pour stocker les font-sizes de base de chaque widget self._base_font_sizes = {} self._font_sizes_extracted = False # Flag pour savoir si on a déjà extrait les tailles # 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 avec un ratio self.adjust_all_font_sizes() def adjust_all_font_sizes(self): """Ajuste dynamiquement les font-sizes de tous les éléments avec un ratio proportionnel""" # 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 # Utiliser la moyenne des deux ratios pour un scaling plus naturel # Ou utiliser le minimum pour éviter le débordement ratio = min(width_ratio,height_ratio) * 1.5 # Limiter le ratio pour éviter des tailles extrêmes ratio = max(0.5, min(ratio, 2.0)) # Entre 50% et 200% # Récupérer tous les widgets des tabs all_widgets = [] if hasattr(self, 'side_menu'): all_widgets = self.side_menu.widgets # Extraire les tailles de base une seule fois if not self._font_sizes_extracted: self._extract_base_font_sizes(all_widgets) self._font_sizes_extracted = True # Parcourir tous les widgets et ajuster leurs tailles for widget in all_widgets: if widget: self._adjust_widget_font_sizes(widget, ratio) def _extract_base_font_sizes(self, widgets): """Extrait les tailles de police de base de tous les widgets une seule fois""" from PyQt6.QtWidgets import QPushButton, QLineEdit, QTextEdit, QComboBox widget_types = [QLabel, QPushButton, QLineEdit, QTextEdit, QComboBox] # Extraire les tailles des boutons d'onglets du side menu if hasattr(self, 'side_menu') and hasattr(self.side_menu, 'buttons'): for button in self.side_menu.buttons: if button: widget_id = id(button) current_style = button.styleSheet() base_size = self._extract_font_size_from_style(current_style) if base_size is None: base_size = 14 # Taille par défaut self._base_font_sizes[widget_id] = base_size for widget in widgets: if not widget: continue for widget_type in widget_types: for child in widget.findChildren(widget_type): widget_id = id(child) # Ignorer les widgets avec un objectName (généralement stylisés spécifiquement) if child.objectName() != "": continue # Extraire la taille de police depuis le stylesheet current_style = child.styleSheet() base_size = self._extract_font_size_from_style(current_style) # Si pas trouvé dans le style, utiliser la taille par défaut if base_size is None: base_size = 14 # Taille par défaut # Stocker la taille de base self._base_font_sizes[widget_id] = base_size def _extract_font_size_from_style(self, style: str) -> Optional[int]: """Extrait la taille de police depuis un stylesheet""" import re # Chercher "font-size: XXpx" match = re.search(r'font-size:\s*(\d+)px', style) if match: return int(match.group(1)) return None def _adjust_widget_font_sizes(self, widget, ratio): """Ajuste les font-sizes de tous les éléments d'un widget avec un ratio proportionnel""" from PyQt6.QtWidgets import QPushButton, QLineEdit, QTextEdit, QComboBox import re # Ajuster les boutons d'onglets du side menu if hasattr(self, 'side_menu') and hasattr(self.side_menu, 'buttons'): for button in self.side_menu.buttons: if button: widget_id = id(button) if widget_id in self._base_font_sizes: base_size = self._base_font_sizes[widget_id] new_size = max(8, int(base_size * ratio)) current_style = button.styleSheet() style_without_font = re.sub(r'font-size:\s*\d+px;?', '', current_style) style_without_font = re.sub(r';+', ';', style_without_font) style_without_font = style_without_font.strip() if style_without_font and not style_without_font.endswith(';'): style_without_font += ';' new_style = f"{style_without_font} font-size: {new_size}px;" button.setStyleSheet(new_style) widget_types = [QLabel, QPushButton, QLineEdit, QTextEdit, QComboBox] for widget_type in widget_types: for child in widget.findChildren(widget_type): widget_id = id(child) # Récupérer la taille de base if widget_id not in self._base_font_sizes: continue # Pas de taille de base, ignorer base_size = self._base_font_sizes[widget_id] # Calculer la nouvelle taille avec le ratio new_size = max(8, int(base_size * ratio)) # Minimum 8px # Appliquer le style en préservant les autres propriétés current_style = child.styleSheet() # Retirer l'ancienne font-size style_without_font = re.sub(r'font-size:\s*\d+px;?', '', current_style) # Nettoyer les points-virgules multiples style_without_font = re.sub(r';+', ';', style_without_font) style_without_font = style_without_font.strip() # Ajouter la nouvelle font-size if style_without_font and not style_without_font.endswith(';'): style_without_font += ';' new_style = f"{style_without_font} font-size: {new_size}px;" child.setStyleSheet(new_style) 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, BorderSide.BOTTOM, TabSide.TOP) self.suggestion_window = SuggestionWindow(self) self.side_menu.add_widget(self.suggestion_window, self.language_manager.get_text("tab_suggestions"), paths.get_asset_svg_path("suggestion"), position=ButtonPosition.CENTER, text_position=TextPosition.BOTTOM) self.settings_window = SettingsWindow(self) self.side_menu.add_widget(self.settings_window, self.language_manager.get_text("tab_settings"), paths.get_asset_svg_path("settings"), position=ButtonPosition.CENTER, text_position=TextPosition.BOTTOM) # 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, self.language_manager.get_text("tab_licensing"), paths.get_asset_svg_path("license"), position=ButtonPosition.END, text_position=TextPosition.BOTTOM) self.setCentralWidget(self.side_menu) def get_tab_widget(self): """Retourne le widget TabsWidget pour permettre le changement d'onglet""" return self.side_menu def update_theme(self) -> None: self.setStyleSheet(self.theme_manager.get_sheet()) def update_language(self) -> None: # Mettre à jour les textes des onglets self.side_menu.update_button_text(0, self.language_manager.get_text("tab_suggestions")) self.side_menu.update_button_text(1, self.language_manager.get_text("tab_settings")) if self.settings_manager.get_config("enable_licensing"): self.side_menu.update_button_text(2, self.language_manager.get_text("tab_licensing"))