Загрузка данных


# Dozor/modules/utils/window_behavior.py

import tkinter as tk


class LevelOneBehavior:
    """
    Поведение Главного окна (Уровень 1). Применяется к объекту класса tk.Tk.
    Движение работает ТОЛЬКО по панели заголовка (title_bar).
    """
    def setup_level_one(self, target_window):
        target_window.resizable(False, False)
        
        # Ищем панель заголовка для перетаскивания главного окна
        try:
            self.drag_widget = target_window.children['title_bar']
        except KeyError:
            self.drag_widget = target_window

        self.drag_widget.bind("<ButtonPress-1>", self.save_offset)
        self.drag_widget.bind("<B1-Motion>", self.drag_window)
        
        target_window.bind("<Double-Button-1>", lambda e: None)
        
        if not hasattr(target_window, '_child_windows'):
            target_window._child_windows = []
            
        self._drag_start_x = 0
        self._drag_start_y = 0

    def save_offset(self, event):
        self._drag_start_x = event.x_root - self.winfo_x()
        self._drag_start_y = event.y_root - self.winfo_y()

    def drag_window(self, event):
        x = event.x_root - self._drag_start_x
        y = event.y_root - self._drag_start_y
        self.geometry(f"+{x}+{y}")
        
        if hasattr(self, '_update_child_positions'):
            self._update_child_positions(x, y)

    def register_child_window(self, window, offset_x=0, offset_y=0):
        self._child_windows.append((window, offset_x, offset_y))

    def do_nothing(self, event=None):
        pass


class LevelTwoBehavior:
    """
    Поведение окон второго уровня (привязаны к родителю).
    Окно центрируется над главным и едет строго вместе с ним.
    РЕЖИМ БЕЗОПАСНОСТИ: Игнорируем кастомные методы регистрации root-а.
    """
    def setup_level_two(self, target_window):
        width, height = int(target_window.winfo_reqwidth()), int(target_window.winfo_reqheight())
        target_window.minsize(width, height)
        target_window.resizable(True, True)
        
        # Просто берем родителя окна (это наш ManagerPanel/Facade)
        parent = target_window.master
        p_x = parent.winfo_rootx()
        p_y = parent.winfo_rooty()
        p_w = parent.winfo_width()
        p_h = parent.winfo_height()
        
        win_w = target_window.winfo_reqwidth()
        win_h = target_window.winfo_reqheight()
        
        x = p_x + (p_w - win_w) // 2
        y = p_y + (p_h - win_h) // 2
        target_window.geometry(f"+{x}+{y}")
        
        target_window.update_idletasks()
        
        # === КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ ===
        # Весь блок с register_child_window УДАЛЕН.
        # Если родитель хочет знать о нас, он сам вызовет wait_window().
    def do_nothing(self, event=None):
        pass

    def save_offset(self, event, window):
        self._start_screen_x = event.x_root
        self._start_screen_y = event.y_root
        self._start_win_x = window.winfo_rootx()
        self._start_win_y = window.winfo_rooty()
        parent = window.master
        self._start_parent_x = parent.winfo_rootx()
        self._start_parent_y = parent.winfo_rooty()

    def drag_with_parent(self, event, child_win, parent_win):
        delta_x = event.x_root - self._start_screen_x
        delta_y = event.y_root - self._start_screen_y

        new_parent_x = self._start_parent_x + delta_x
        new_parent_y = self._start_parent_y + delta_y
        parent_win.geometry(f"+{new_parent_x}+{new_parent_y}")

        offset_x = self._start_win_x - self._start_parent_x
        offset_y = self._start_win_y - self._start_parent_y
        
        target_child_x = new_parent_x + offset_x
        target_child_y = new_parent_y + offset_y
        child_win.geometry(f"+{target_child_x}+{target_child_y}")


class LevelThreeBehavior:
    """
    Поведение окон третьего уровня (Менеджер групп).
    Свободное перемещение. 
    Чтобы выделить текст, просто кликай мышкой.
    Чтобы двигать окно, зажми ALT + ЛКМ.
    """
    def setup_level_three(self, target_window):
        width, height = int(target_window.winfo_reqwidth()), int(target_window.winfo_reqheight())
        target_window.minsize(width, height)
        target_window.resizable(True, True)
        target_window.geometry("+0+0")
        
        target_window.protocol("WM_MINIMIZE_WINDOW", self.do_nothing)
        target_window.protocol("WM_DELETE_WINDOW", target_window.destroy)
        
        # Снимаем старые бинды
        target_window.unbind("<ButtonPress-1>")
        target_window.unbind("<B1-Motion>")
        
        # 1. Сохраняем точку старта. Передаем 'target_window' вторым аргументом через lambda
        target_window.bind(
            "<ButtonPress-1>", 
            lambda e, tw=target_window: self.save_offset(e, tw), 
            add='+'
        )
        
        # 2. Движение работает ТОЛЬКО если зажата клавиша Alt (маска 0x0004)
        target_window.bind(
            "<B1-Motion>", 
            lambda e, tw=target_window: self.drag_free(e, tw) if e.state & 0x0004 else "", 
            add='+'
        )

    def do_nothing(self, event=None):
        pass

    def save_offset(self, event, window):
        # Запоминаем ГДЕ НА ЭКРАНЕ была нажата мышь
        self._start_screen_x = event.x_root
        self._start_screen_y = event.y_root
        
        # Запоминаем, где В ЭТОТ МОМЕНТ было окно (его угол) ПО ЭКРАНУ
        self._start_win_x = window.winfo_rootx()
        self._start_win_y = window.winfo_rooty()

    def drag_free(self, event, window):
        dx = event.x_root - self._start_screen_x
        dy = event.y_root - self._start_screen_y
        new_x = self._start_win_x + dx
        new_y = self._start_win_y + dy
        window.geometry(f"+{new_x}+{new_y}")