Spaces:
Running
Running
// Глобальный массив для хранения всех окон | |
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); | |
} | |
}); | |
}); | |
} |