win / js /windows.js
igorvkarpov's picture
Upload 24 files
cdf0905 verified
// Глобальный массив для хранения всех окон
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);
}
});
});
}