""" Main window geometry and resize mixin. Window flags, save/restore geometry, compact mode, responsive minimum, resize handling. """ from PySide6.QtWidgets import QMainWindow, QApplication from PySide6.QtCore import Qt, QTimer, QRect from jackify.frontends.gui.utils import get_screen_geometry, set_responsive_minimum ENABLE_WINDOW_HEIGHT_ANIMATION = False def _debug_print(message): from jackify.backend.handlers.config_handler import ConfigHandler ch = ConfigHandler() if ch.get('debug_mode', False): print(message) class MainWindowGeometryMixin: """Mixin for window geometry, save/restore, compact mode, and resize behavior.""" def _apply_standard_window_flags(self): window_flags = self.windowFlags() window_flags |= ( Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint ) window_flags &= ~Qt.CustomizeWindowHint self.setWindowFlags(window_flags) def _restore_geometry(self): width, height = self._calculate_initial_window_size() height = min(height, self._compact_height) self.resize(width, height) self._center_on_screen(width, height) def _save_geometry_on_quit(self): if self._is_compact_mode(): self._save_geometry() else: from PySide6.QtCore import QSettings settings = QSettings("Jackify", "Jackify") settings.remove("windowGeometry") def _is_compact_mode(self) -> bool: try: if hasattr(self, 'install_modlist_screen') and hasattr(self.install_modlist_screen, 'show_details_checkbox'): if self.install_modlist_screen.show_details_checkbox.isChecked(): return False if hasattr(self, 'install_ttw_screen') and hasattr(self.install_ttw_screen, 'show_details_checkbox'): if self.install_ttw_screen.show_details_checkbox.isChecked(): return False if hasattr(self, 'configure_new_modlist_screen') and hasattr(self.configure_new_modlist_screen, 'show_details_checkbox'): if self.configure_new_modlist_screen.show_details_checkbox.isChecked(): return False if hasattr(self, 'configure_existing_modlist_screen') and hasattr(self.configure_existing_modlist_screen, 'show_details_checkbox'): if self.configure_existing_modlist_screen.show_details_checkbox.isChecked(): return False except Exception: pass return True def _save_geometry(self): from PySide6.QtCore import QSettings settings = QSettings("Jackify", "Jackify") settings.setValue("windowGeometry", self.saveGeometry()) def apply_responsive_minimum(self, min_width: int = 1100, min_height: int = 600): set_responsive_minimum(self, min_width=min_width, min_height=min_height, margin=self._window_margin) def _calculate_initial_window_size(self): _, _, screen_width, screen_height = get_screen_geometry(self) if not screen_width or not screen_height: return (self._base_min_width, self._base_min_height) width = min( max(self._base_min_width, int(screen_width * 0.85)), screen_width - self._window_margin ) height = min( max(self._base_min_height, int(screen_height * 0.75)), screen_height - self._window_margin ) return (width, height) def _center_on_screen(self, width: int, height: int): _, _, screen_width, screen_height = get_screen_geometry(self) if not screen_width or not screen_height: return x = max(0, (screen_width - width) // 2) y = max(0, (screen_height - height) // 2) self.move(x, y) def _ensure_within_available_geometry(self): from PySide6.QtCore import QRect _, _, screen_width, screen_height = get_screen_geometry(self) if not screen_width or not screen_height: return current_geometry = self.geometry() new_width = min(current_geometry.width(), screen_width - self._window_margin) new_height = min(current_geometry.height(), screen_height - self._window_margin) new_width = max(new_width, self.minimumWidth()) new_height = max(new_height, self.minimumHeight()) new_x = min(max(current_geometry.x(), 0), screen_width - new_width) new_y = min(max(current_geometry.y(), 0), screen_height - new_height) self.setGeometry(new_x, new_y, new_width, new_height) def _on_resize_event_geometry(self, event): super().resizeEvent(event) if self._is_compact_mode(): if not hasattr(self, '_geometry_save_timer'): self._geometry_save_timer = QTimer() self._geometry_save_timer.setSingleShot(True) self._geometry_save_timer.timeout.connect(self._save_geometry) self._geometry_save_timer.stop() self._geometry_save_timer.start(500) def _geometry_show_event(self, event): super().showEvent(event) if not self._initial_show_adjusted: self._initial_show_adjusted = True if not (hasattr(self, 'system_info') and self.system_info.is_steamdeck): self.setWindowState(Qt.WindowNoState) self.apply_responsive_minimum(self._base_min_width, self._base_min_height) self._ensure_within_available_geometry() def _maintain_fullscreen_on_deck(self, index): if hasattr(self, 'system_info') and self.system_info.is_steamdeck: if not self.isMaximized(): self.showMaximized() def _on_child_resize_request(self, mode: str): _debug_print(f"DEBUG: _on_child_resize_request called with mode='{mode}', current_size={self.size()}") try: if self.system_info and self.system_info.is_steamdeck: _debug_print("DEBUG: Steam Deck detected, ignoring resize request") try: if hasattr(self, 'install_ttw_screen') and self.install_ttw_screen.show_details_checkbox: self.install_ttw_screen.show_details_checkbox.setVisible(False) except Exception: pass return except Exception: pass if mode == "expand": target_height = self._compact_height + self._details_extra_height self._resize_height(target_height) elif mode == "collapse" or mode == "compact": self._resize_height(self._compact_height) else: self.apply_responsive_minimum(self._base_min_width, self._base_min_height) def _resize_height(self, requested_height: int): target_height = self._clamp_height_to_screen(requested_height) self.apply_responsive_minimum(self._base_min_width, self._base_min_height) if ENABLE_WINDOW_HEIGHT_ANIMATION: self._animate_height(target_height) return geom = self.geometry() new_y = geom.y() _, _, _, screen_height = get_screen_geometry(self) max_bottom = max(self._base_min_height, screen_height - self._window_margin) if new_y + target_height > max_bottom: new_y = max(0, max_bottom - target_height) self._programmatic_resize = True self.setGeometry(geom.x(), new_y, geom.width(), target_height) QTimer.singleShot(100, lambda: setattr(self, '_programmatic_resize', False)) def _clamp_height_to_screen(self, requested_height: int) -> int: _, _, _, screen_height = get_screen_geometry(self) available = max(self._base_min_height, screen_height - self._window_margin) return max(self._base_min_height, min(requested_height, available)) def _animate_height(self, target_height: int, duration_ms: int = 180): try: from PySide6.QtCore import QEasingCurve, QPropertyAnimation, QRect except Exception: before = self.size() self._programmatic_resize = True self.resize(self.size().width(), target_height) _debug_print(f"DEBUG: Animated fallback resize from {before} to {self.size()}") QTimer.singleShot(100, lambda: setattr(self, '_programmatic_resize', False)) return start_rect = self.geometry() end_rect = QRect(start_rect.x(), start_rect.y(), start_rect.width(), self._clamp_height_to_screen(target_height)) screen = QApplication.primaryScreen() if screen: screen_geometry = screen.availableGeometry() would_be_bottom = start_rect.y() + target_height if would_be_bottom > screen_geometry.bottom(): new_y = screen_geometry.bottom() - target_height if new_y < screen_geometry.top(): new_y = screen_geometry.top() end_rect.moveTop(new_y) self._resize_anim = QPropertyAnimation(self, b"geometry") self._resize_anim.setDuration(duration_ms) self._resize_anim.setEasingCurve(QEasingCurve.OutCubic) self._resize_anim.setStartValue(start_rect) self._resize_anim.setEndValue(end_rect) self._programmatic_resize = True self._resize_anim.finished.connect(lambda: setattr(self, '_programmatic_resize', False)) self._resize_anim.start()