// Глобальный массив для хранения всех окон let windows = []; let activeWindowId = null; let zIndex = 100; // Глобальное хранилище открытых приложений (appId -> windowId) const openApps = {}; // Функция для создания нового окна function createWindow(options = {}) { console.log('Создание окна с параметрами:', options); const { title = 'Новое окно', icon = 'default-app-icon.png', content = '', width = 600, height = 400, x = 100, y = 100, isActive = true, appId } = options; // Проверяем, существует ли уже открытое окно с таким appId if (appId && openApps[appId]) { const existingWindowId = openApps[appId]; const existingWindow = windows.find(w => w.id === existingWindowId); // Если окно существует, активируем его и возвращаем if (existingWindow) { console.log(`Приложение ${appId} уже открыто, активируем существующее окно:`, existingWindowId); // Если окно свёрнуто, восстанавливаем его if (existingWindow.isMinimized) { restoreWindow(existingWindowId); } // Активируем окно activateWindow(existingWindowId); return existingWindow; } } // Создаем уникальный ID для окна const windowId = `window-${Date.now()}-${Math.floor(Math.random() * 1000)}`; // Создаем DOM элемент окна const windowElement = document.createElement('div'); windowElement.className = `window ${isActive ? 'active' : ''}`; windowElement.id = windowId; windowElement.dataset.appId = appId || ''; windowElement.style.width = `${width}px`; windowElement.style.height = `${height}px`; windowElement.style.left = `${x}px`; windowElement.style.top = `${y}px`; windowElement.style.zIndex = isActive ? ++zIndex : zIndex; // Создаем заголовок окна const windowHeader = document.createElement('div'); windowHeader.className = 'window-header'; const windowTitle = document.createElement('div'); windowTitle.className = 'window-title'; const iconImg = document.createElement('img'); iconImg.src = `src/images/${icon}`; iconImg.alt = title; const titleText = document.createElement('span'); titleText.textContent = title; windowTitle.appendChild(iconImg); windowTitle.appendChild(titleText); // Создаем кнопки управления окном const windowControls = document.createElement('div'); windowControls.className = 'window-controls'; const minimizeButton = document.createElement('div'); minimizeButton.className = 'window-control minimize'; minimizeButton.innerHTML = '–'; minimizeButton.title = 'Свернуть'; const maximizeButton = document.createElement('div'); maximizeButton.className = 'window-control maximize'; maximizeButton.innerHTML = '☐'; maximizeButton.title = 'Развернуть'; const closeButton = document.createElement('div'); closeButton.className = 'window-control close'; closeButton.innerHTML = '✕'; closeButton.title = 'Закрыть'; windowControls.appendChild(minimizeButton); windowControls.appendChild(maximizeButton); windowControls.appendChild(closeButton); windowHeader.appendChild(windowTitle); windowHeader.appendChild(windowControls); // Создаем содержимое окна const windowContent = document.createElement('div'); windowContent.className = 'window-content'; windowContent.innerHTML = content; // Собираем окно вместе windowElement.appendChild(windowHeader); windowElement.appendChild(windowContent); // Добавляем окно в DOM const container = document.getElementById('windows-container'); console.log('Контейнер для окон:', container); if (container) { container.appendChild(windowElement); console.log('Окно добавлено в DOM'); } else { console.error('Не найден контейнер для окон с id="windows-container"'); return null; } // Добавляем окно в массив окон const windowObject = { id: windowId, element: windowElement, appId: appId || '', title, icon, isMinimized: false, isMaximized: false, // Сохраняем оригинальные размеры и позицию для восстановления из полноэкранного режима originalSize: { width, height, x, y } }; windows.push(windowObject); console.log('Окно добавлено в массив, всего окон:', windows.length); // Если окно активное, делаем его активным if (isActive) { activateWindow(windowId); } // Сохраняем ID окна для приложения, чтобы избежать дублирования if (appId) { openApps[appId] = windowId; console.log(`Приложение ${appId} зарегистрировано с окном ${windowId}`); } // Добавляем кнопку в панель задач addWindowToTaskbar(windowObject); // Добавляем обработчики событий setupWindowEvents(windowObject); return windowObject; } // Функция для активации окна function activateWindow(windowId) { console.log('Активация окна:', windowId); // Если активно другое окно, делаем его неактивным if (activeWindowId && activeWindowId !== windowId) { const activeWindow = document.getElementById(activeWindowId); if (activeWindow) { activeWindow.classList.remove('active'); } // Обновляем стили в панели задач const taskbarItem = document.querySelector(`.taskbar-program[data-window-id="${activeWindowId}"]`); if (taskbarItem) { taskbarItem.classList.remove('active'); } } // Активируем новое окно const windowToActivate = document.getElementById(windowId); if (windowToActivate) { windowToActivate.classList.add('active'); windowToActivate.style.zIndex = ++zIndex; activeWindowId = windowId; // Обновляем стили в панели задач const taskbarItem = document.querySelector(`.taskbar-program[data-window-id="${windowId}"]`); if (taskbarItem) { taskbarItem.classList.add('active'); } } else { console.error('Не найдено окно для активации с id', windowId); } } // Функция для добавления окна в панель задач function addWindowToTaskbar(windowObject) { const taskbarPrograms = document.getElementById('taskbar-programs'); if (!taskbarPrograms) { console.error('Не найден контейнер для программ в панели задач'); return; } // Проверяем, существует ли уже программа в панели задач const existingTaskbarItem = document.querySelector(`.taskbar-program[data-app-id="${windowObject.appId}"]`); if (existingTaskbarItem && windowObject.appId) { // Если программа уже есть, добавляем к ней ссылку на новое окно existingTaskbarItem.dataset.windowId = windowObject.id; existingTaskbarItem.classList.add('active'); } else { // Если программы нет, создаем новую иконку в панели задач const taskbarItem = document.createElement('div'); taskbarItem.className = 'taskbar-program active'; taskbarItem.dataset.windowId = windowObject.id; taskbarItem.dataset.appId = windowObject.appId || ''; const iconImg = document.createElement('img'); iconImg.src = `src/images/${windowObject.icon}`; iconImg.alt = windowObject.title; taskbarItem.appendChild(iconImg); taskbarPrograms.appendChild(taskbarItem); // Добавляем обработчик события для кнопки в панели задач taskbarItem.addEventListener('click', () => { console.log('Клик по кнопке в панели задач:', windowObject.id); // Найдем окно по ID const window = windows.find(w => w.id === windowObject.id); if (!window) { console.error('Не найдено окно с ID:', windowObject.id); return; } console.log('Состояние окна:', window.isMinimized ? 'свернуто' : 'открыто', 'активно:', activeWindowId === window.id); // Проверяем, свернуто ли окно if (window.isMinimized) { console.log('Восстанавливаем свернутое окно:', window.id); restoreWindow(window.id); } // Если окно активно, сворачиваем его else if (activeWindowId === window.id) { console.log('Сворачиваем активное окно:', window.id); minimizeWindow(window.id); } // Если окно не активно, делаем его активным else { console.log('Активируем неактивное окно:', window.id); activateWindow(window.id); } }); } } // Функция для сворачивания окна function minimizeWindow(windowId) { const windowObj = windows.find(w => w.id === windowId); if (windowObj) { windowObj.isMinimized = true; windowObj.element.classList.add('minimized'); // Если это активное окно, сбрасываем активное окно if (activeWindowId === windowId) { activeWindowId = null; } // Обновляем стили в панели задач const taskbarItem = document.querySelector(`.taskbar-program[data-window-id="${windowId}"]`); if (taskbarItem) { taskbarItem.classList.remove('active'); } } } // Функция для восстановления окна function restoreWindow(windowId) { const windowObj = windows.find(w => w.id === windowId); if (windowObj) { windowObj.isMinimized = false; windowObj.element.classList.remove('minimized'); activateWindow(windowId); } } // Функция для разворачивания окна на весь экран function maximizeWindow(windowId) { const windowObj = windows.find(w => w.id === windowId); if (!windowObj) return; const container = document.getElementById('windows-container'); if (!container) return; const windowElement = windowObj.element; // Если окно уже развернуто, восстанавливаем его оригинальный размер if (windowObj.isMaximized) { console.log('Восстановление окна из полноэкранного режима:', windowId); // Восстанавливаем оригинальные размеры и позицию windowElement.style.width = `${windowObj.originalSize.width}px`; windowElement.style.height = `${windowObj.originalSize.height}px`; windowElement.style.left = `${windowObj.originalSize.x}px`; windowElement.style.top = `${windowObj.originalSize.y}px`; // Обновляем иконку кнопки разворачивания const maximizeButton = windowElement.querySelector('.window-control.maximize'); if (maximizeButton) { maximizeButton.innerHTML = '☐'; // Иконка "развернуть" maximizeButton.title = 'Развернуть'; } windowObj.isMaximized = false; windowElement.classList.remove('maximized'); } // Иначе разворачиваем окно на весь экран else { console.log('Разворачивание окна на весь экран:', windowId); // Сохраняем текущие размеры и позицию перед разворачиванием // (только если они еще не сохранены) if (!windowObj.originalSize) { windowObj.originalSize = { width: parseInt(windowElement.style.width), height: parseInt(windowElement.style.height), x: parseInt(windowElement.style.left), y: parseInt(windowElement.style.top) }; } // Получаем размеры контейнера (рабочего стола) const containerRect = container.getBoundingClientRect(); // Устанавливаем размеры окна равными размерам контейнера windowElement.style.width = `${containerRect.width}px`; windowElement.style.height = `${containerRect.height}px`; windowElement.style.left = '0px'; windowElement.style.top = '0px'; // Обновляем иконку кнопки разворачивания const maximizeButton = windowElement.querySelector('.window-control.maximize'); if (maximizeButton) { maximizeButton.innerHTML = '☐☐'; // Иконка "восстановить" maximizeButton.title = 'Восстановить'; } windowObj.isMaximized = true; windowElement.classList.add('maximized'); } // Активируем окно activateWindow(windowId); } // Функция для закрытия окна function closeWindow(windowId) { const windowIndex = windows.findIndex(w => w.id === windowId); if (windowIndex !== -1) { const windowObj = windows[windowIndex]; // Удаляем окно из DOM if (windowObj.element) { windowObj.element.remove(); } // Если это было активное окно, сбрасываем активное окно if (activeWindowId === windowId) { activeWindowId = null; } // Удаляем регистрацию открытого приложения if (windowObj.appId && openApps[windowObj.appId] === windowId) { delete openApps[windowObj.appId]; console.log(`Удалена регистрация приложения ${windowObj.appId}`); } // Удаляем из массива окон windows.splice(windowIndex, 1); // Проверяем, есть ли еще окна с таким же appId const sameAppWindows = windows.filter(w => w.appId === windowObj.appId && w.appId !== ''); // Если других окон с таким же appId нет, удаляем кнопку из панели задач if (sameAppWindows.length === 0) { const taskbarItem = document.querySelector(`.taskbar-program[data-app-id="${windowObj.appId}"]`); if (taskbarItem) { taskbarItem.remove(); } } else { // Иначе обновляем ссылку на последнее окно этого приложения const lastWindow = sameAppWindows[sameAppWindows.length - 1]; const taskbarItem = document.querySelector(`.taskbar-program[data-app-id="${windowObj.appId}"]`); if (taskbarItem) { taskbarItem.dataset.windowId = lastWindow.id; } } } } // Функция для настройки событий окна function setupWindowEvents(windowObj) { const windowElement = windowObj.element; const header = windowElement.querySelector('.window-header'); const minimizeButton = windowElement.querySelector('.window-control.minimize'); const maximizeButton = windowElement.querySelector('.window-control.maximize'); const closeButton = windowElement.querySelector('.window-control.close'); // Добавляем возможность перетаскивания окна let isDragging = false; let offsetX, offsetY; // Функция для обработки начала перетаскивания (как для мыши, так и для сенсорного ввода) const handleStartDrag = (clientX, clientY, target) => { // Если окно развернуто на весь экран, не даем его перетаскивать if (windowObj.isMaximized) return; if (target === header || target.closest('.window-title')) { isDragging = true; offsetX = clientX - windowElement.getBoundingClientRect().left; offsetY = clientY - windowElement.getBoundingClientRect().top; // Активируем окно при начале перетаскивания activateWindow(windowObj.id); } }; // Функция для обработки перемещения (как для мыши, так и для сенсорного ввода) const handleDrag = (clientX, clientY) => { if (!isDragging) return; // Получаем размеры контейнера рабочего стола const container = document.getElementById('windows-container'); const containerRect = container.getBoundingClientRect(); // Получаем размеры окна const windowRect = windowElement.getBoundingClientRect(); const windowWidth = windowRect.width; const windowHeight = windowRect.height; // Вычисляем новые координаты с учетом ограничений let newX = clientX - offsetX; let newY = clientY - offsetY; // Ограничиваем перемещение окна, чтобы оно не выходило за пределы рабочего стола // Задаем минимальное значение заголовка, которое должно быть видимым (чтобы можно было перетащить окно обратно) const minVisibleHeader = 30; // минимальная видимая часть заголовка окна // Ограничение по X (горизонтали) if (newX < -windowWidth + minVisibleHeader) { newX = -windowWidth + minVisibleHeader; } else if (newX > containerRect.width - minVisibleHeader) { newX = containerRect.width - minVisibleHeader; } // Ограничение по Y (вертикали) if (newY < 0) { newY = 0; } else if (newY > containerRect.height - minVisibleHeader) { newY = containerRect.height - minVisibleHeader; } // Применяем новые координаты windowElement.style.left = `${newX}px`; windowElement.style.top = `${newY}px`; }; // Функция для обработки окончания перетаскивания const handleEndDrag = () => { isDragging = false; }; // Обработчики событий мыши header.addEventListener('mousedown', (e) => { handleStartDrag(e.clientX, e.clientY, e.target); }); document.addEventListener('mousemove', (e) => { handleDrag(e.clientX, e.clientY); }); document.addEventListener('mouseup', handleEndDrag); // Обработчики сенсорных событий header.addEventListener('touchstart', (e) => { const touch = e.touches[0]; handleStartDrag(touch.clientX, touch.clientY, e.target); e.preventDefault(); // Предотвращаем скролл страницы }); document.addEventListener('touchmove', (e) => { if (isDragging) { const touch = e.touches[0]; handleDrag(touch.clientX, touch.clientY); e.preventDefault(); // Предотвращаем скролл страницы } }); document.addEventListener('touchend', handleEndDrag); document.addEventListener('touchcancel', handleEndDrag); // Добавляем двойной клик на заголовок для разворачивания/восстановления header.addEventListener('dblclick', () => { maximizeWindow(windowObj.id); }); // Также добавляем обработчик двойного тапа для мобильных устройств let lastTap = 0; header.addEventListener('touchend', (e) => { const currentTime = new Date().getTime(); const tapLength = currentTime - lastTap; if (tapLength < 300 && tapLength > 0) { // Двойной тап - разворачиваем/восстанавливаем окно maximizeWindow(windowObj.id); e.preventDefault(); } lastTap = currentTime; }); // Активируем окно при клике на него windowElement.addEventListener('mousedown', () => { activateWindow(windowObj.id); }); windowElement.addEventListener('touchstart', () => { activateWindow(windowObj.id); }); // Обработчики для кнопок управления окном if (minimizeButton) { minimizeButton.addEventListener('click', () => { minimizeWindow(windowObj.id); }); minimizeButton.addEventListener('touchend', (e) => { minimizeWindow(windowObj.id); e.preventDefault(); }); } if (maximizeButton) { maximizeButton.addEventListener('click', () => { maximizeWindow(windowObj.id); }); maximizeButton.addEventListener('touchend', (e) => { maximizeWindow(windowObj.id); e.preventDefault(); }); } if (closeButton) { closeButton.addEventListener('click', () => { closeWindow(windowObj.id); }); closeButton.addEventListener('touchend', (e) => { closeWindow(windowObj.id); e.preventDefault(); }); } // Добавляем поддержку изменения размера окна на мобильных устройствах // с помощью жеста "pinch" (щипка) let initialDistance = 0; let initialWidth = 0; let initialHeight = 0; windowElement.addEventListener('touchstart', (e) => { if (e.touches.length === 2) { // Два пальца - жест изменения размера const touch1 = e.touches[0]; const touch2 = e.touches[1]; // Вычисляем расстояние между пальцами initialDistance = Math.hypot( touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY ); // Запоминаем начальные размеры окна initialWidth = windowElement.offsetWidth; initialHeight = windowElement.offsetHeight; e.preventDefault(); } }); windowElement.addEventListener('touchmove', (e) => { if (e.touches.length === 2 && initialDistance > 0) { // Два пальца - жест изменения размера const touch1 = e.touches[0]; const touch2 = e.touches[1]; // Вычисляем новое расстояние между пальцами const newDistance = Math.hypot( touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY ); // Вычисляем коэффициент масштабирования const scale = newDistance / initialDistance; // Применяем новые размеры с ограничениями const newWidth = Math.max(300, Math.min(initialWidth * scale, window.innerWidth)); const newHeight = Math.max(200, Math.min(initialHeight * scale, window.innerHeight - 50)); windowElement.style.width = `${newWidth}px`; windowElement.style.height = `${newHeight}px`; e.preventDefault(); } }); windowElement.addEventListener('touchend', () => { initialDistance = 0; }); } // Инициализация системы окон function initializeWindows() { console.log('Инициализация системы окон'); // Обновляем обработчики для существующих кнопок в панели задач setTimeout(() => { updateTaskbarHandlers(); }, 500); // Настраиваем глобальный обработчик клика для снятия активации со всех окон при клике на рабочий стол const windowsContainer = document.getElementById('windows-container'); if (windowsContainer) { windowsContainer.addEventListener('mousedown', (e) => { if (e.target === document.getElementById('windows-container')) { // Если клик был непосредственно на рабочем столе if (activeWindowId) { const activeWindow = document.getElementById(activeWindowId); if (activeWindow) { activeWindow.classList.remove('active'); } // Обновляем стили в панели задач const taskbarItem = document.querySelector(`.taskbar-program[data-window-id="${activeWindowId}"]`); if (taskbarItem) { taskbarItem.classList.remove('active'); } activeWindowId = null; } } }); } else { console.error('Не найден контейнер для окон с id="windows-container"'); } } // Функция для обновления обработчиков событий для кнопок в панели задач function updateTaskbarHandlers() { const taskbarItems = document.querySelectorAll('.taskbar-program'); console.log('Найдено кнопок в панели задач:', taskbarItems.length); taskbarItems.forEach(item => { const windowId = item.dataset.windowId; if (!windowId) return; console.log('Добавление обработчика для кнопки в панели задач:', windowId); // Удаляем старые обработчики, чтобы избежать дублирования const newItem = item.cloneNode(true); if (item.parentNode) { item.parentNode.replaceChild(newItem, item); } newItem.addEventListener('click', () => { console.log('Клик по кнопке в панели задач:', windowId); // Проверим, есть ли такое окно const windowObj = windows.find(w => w.id === windowId); if (!windowObj) { console.error('Не найдено окно с ID:', windowId); return; } console.log('Состояние окна:', windowObj.isMinimized ? 'свернуто' : 'открыто', 'активно:', activeWindowId === windowObj.id); // Проверяем, свернуто ли окно if (windowObj.isMinimized) { console.log('Восстанавливаем свернутое окно:', windowObj.id); restoreWindow(windowObj.id); } // Если окно активно, сворачиваем его else if (activeWindowId === windowObj.id) { console.log('Сворачиваем активное окно:', windowObj.id); minimizeWindow(windowObj.id); } // Если окно не активно, делаем его активным else { console.log('Активируем неактивное окно:', windowObj.id); activateWindow(windowObj.id); } }); }); }