Исходный код EJIO.ctk_extender.window.session

## /ctk_extender/window/session.py

from pathlib import Path
from typing import Any, TypedDict
from loguru import logger

from EJIO.utils import StaticClass, Disposable, SystemProperties, SystemPaths, save, load

__all__ = ['WindowSessionManager']


class WindowGeometryData(TypedDict):
    width: int
    height: int
    x: int
    y: int


[документация] class WindowSessionManager(StaticClass, Disposable): """ Статический менеджер сохранения и восстановления геометрии окон. Защищает интерфейс от 'вылета' окон за пределы физических экранов. """ __slots__ = () _cached_sessions: dict[str, dict[str, Any]] = {} _is_loaded: bool = False @classmethod def _get_session_file_path(cls) -> Path: """Возвращает изолированный кроссплатформенный путь к файлу сессий приложения.""" app_data = SystemPaths.app_data try: name = SETTINGS.get('APPLICATION').get('NAME') except Exception: name = 'DefaultEJIOApp' app_folder = app_data / name#ApplicationCore.settings.get('application', {}).get('name', 'DefaultEJIOApp') app_folder.mkdir(parents=True, exist_ok=True) return app_folder / "window_sessions.json"
[документация] @classmethod def load_sessions(cls) -> None: """Лениво считывает файл сохраненных сессий с диска.""" if cls._is_loaded: return file_path = cls._get_session_file_path() if file_path.exists(): try: data = load(dict, file_path) if not isinstance(data, dict): raise TypeError(f"File {file_path} is corrupted") cls._cached_sessions = data logger.debug(f"Window geometry sessions loaded successfully from '{file_path}'") except Exception as e: logger.error(f"Failed to read windows sessions file: {e}") cls._cached_sessions = {} cls._is_loaded = True
[документация] @classmethod def save_sessions(cls) -> None: """Записывает кэшированные сессии окон на диск.""" file_path = cls._get_session_file_path() try: save(cls._cached_sessions, file_path) except Exception as e: logger.error(f"Error saving window sessions to disk: {e}")
[документация] @classmethod def get_stored_geometry(cls, window_name: str) -> WindowGeometryData | None: """ Возвращает сохраненную геометрию окна, если она валидна и полностью помещается в границы активных мониторов. """ cls.load_sessions() if window_name not in cls._cached_sessions: return None geo: WindowGeometryData = cls._cached_sessions[window_name] # Многомониторная кроссплатформенная валидация координат if cls._is_position_valid(geo["x"], geo["y"]): return geo logger.warning( f"Saved window position '{window_name}' ({geo['x']},{geo['y']})" f" is invalid for the current monitor configuration. Reset to center." ) return None
[документация] @classmethod def store_geometry(cls, window_name: str, width: int, height: int, x: int, y: int) -> None: """Сохраняет геометрию окна в рантайм-кэш менеджера сессий.""" cls.load_sessions() cls._cached_sessions[window_name] = { "width": width, "height": height, "x": x, "y": y }
@classmethod def _is_position_valid(cls, x: int, y: int) -> bool: """ Низкоуровневая кроссплатформенная проверка: пересекается ли точка (X, Y) хотя бы с одним активным монитором в ОС. """ sys_name = SystemProperties.os # На Windows вызываем высокоэффективную MonitorFromPoint из user32.dll if sys_name == "windows": try: import ctypes # MONITOR_DEFAULTTONULL = 0 (возвращает 0, если точка вне всех мониторов) # Передаем координаты как структуру POINT (2 лонга) class POINT(ctypes.Structure): _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)] h_monitor = ctypes.windll.user32.MonitorFromPoint(POINT(x, y), 0) return h_monitor != 0 except Exception: pass # Для linux и macos: # Мы запрашиваем у графического сервера операционной системы, существует ли # физический виртуальный экран под точкой x, y. try: from ..app.base import Application root = Application().context_root if root is not None: # Метод winfo_containing запрашивает у X11/Wayland/Cocoa виджет под координатами. # Если точка находится вне всех подключенных мониторов, метод вернет None или упадет. # Чтобы проверить пустую область экрана, мы смотрим границы виртуального vroot. v_width = root.winfo_vrootwidth() v_height = root.winfo_vrootheight() # Дополнительная проверка вхождения в геометрию виртуального рабочего стола POSIX if x >= 0 and y >= 0 and x < v_width and y < v_height: return True except Exception: pass # Резервный кроссплатформенный вариант для Linux/Mac: # Проверяем, входят ли координаты в виртуальное пространство экранов X11/Cocoa return x >= 0 and y >= 0
[документация] @classmethod def clear(cls) -> None: """Реализация Disposable: сбрасывает кэш сессий.""" cls._cached_sessions.clear() cls._is_loaded = False