/* ============================================================ AURA OS — Pencere Yöneticisi (Window Manager) createWindow / focus / minimize / restore / close / drag / z-sırası ============================================================ */ // Global uygulama kayıt defteri const AuraApps = {}; // Pencere yöneticisi durumu const Wm = { windows: [], // tüm pencere kayıtları zCounter: 10, // z-index sayaç (her focus artar) activeWin: null, // şu an üstte olan pencere nextId: 1, }; function registerApp(appId, config) { AuraApps[appId] = config; // console.log('[AuraApps] kayıtlı:', appId); } /** * Yeni bir pencere oluşturup ekran katmanına ekler. * @param {object} opts * appId: string — uygulama kimliği (AuraApps kaydı) * title: string — başlık * icon: string — emoji ikon * width,height: pencere boyut * x, y: başlangıç konumu (opsiyonel) * content: html string (body içeriği) * onReady: function(winBody) — içeriğin DOM'a bağlanmasından sonra çağrılır */ function createWindow(opts) { const app = AuraApps[opts.appId]; if (!app) { console.error('[WM] Bilinmeyen uygulama:', opts.appId); return null; } // Varsayılanlar const width = opts.width || app.defaultWidth || 560; const height = opts.height || app.defaultHeight || 400; // Konum: verilmediyse kademeli merkez açılım const vw = window.innerWidth; const vh = window.innerHeight - 50; // taskbar payı const baseX = typeof opts.x === 'number' ? opts.x : Math.max(30, Math.round((vw - width) / 2)); const baseY = typeof opts.y === 'number' ? opts.y : Math.max(20, Math.round((vh - height) / 3)); const offset = (Wm.windows.length % 8) * 22; const x = Math.min(baseX + offset, vw - width - 20); const y = Math.min(baseY + offset, vh - height - 20); const id = 'win-' + (Wm.nextId++); const zIndex = ++Wm.zCounter; // Pencere kök elemanı const win = document.createElement('div'); win.className = 'aura-window active'; win.id = id; win.dataset.appId = opts.appId; win.style.left = Math.max(6, x) + 'px'; win.style.top = Math.max(6, y) + 'px'; win.style.width = width + 'px'; win.style.height = height + 'px'; win.style.zIndex = zIndex; // Baslık çubuğu const titlebar = document.createElement('div'); titlebar.className = 'window-titlebar'; titlebar.innerHTML = '' + (opts.icon || app.icon || '🪟') + '' + '' + escapeHtml(opts.title || app.name || opts.appId) + '' + '
' + '' + '' + '
'; // Gövde const body = document.createElement('div'); body.className = 'window-body'; win.appendChild(titlebar); win.appendChild(body); // Kayıt nesnesi const record = { id, appId: opts.appId, title: opts.title || app.name, icon: opts.icon || app.icon, el: win, titlebar, body, minimized: false, metadata: opts.metadata || null, }; Wm.windows.push(record); // Pencereyi katmana ekle const layer = document.getElementById('window-layer'); layer.appendChild(win); record.launchTime = Date.now(); // ---- Başlık çubuğundan sürükleme ---- setupDrag(record); // ---- Odaklama olayı: pencereye tıklandığında en öne ---- win.addEventListener('mousedown', (e) => { if (record.minimized) return; focusWindow(record.id); }); // Başlık çubuğu drag başlarken de öne alınmalı titlebar.addEventListener('mousedown', (e) => { if (record.minimized) return; focusWindow(record.id); }); // ---- Buton davranışları ---- const minBtn = titlebar.querySelector('.btn-min'); const closeBtn = titlebar.querySelector('.btn-close'); minBtn.addEventListener('click', (e) => { e.stopPropagation(); minimizeWindow(record.id); }); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); closeWindow(record.id); }); // ---- İçeriği doldur ---- if (typeof opts.content === 'string') { body.innerHTML = opts.content; } // ---- Görev çubuğu butonunu oluştur ---- window.Taskbar && window.Taskbar.addTaskButton(record); record.taskBtn = window.Taskbar && window.Taskbar.getTaskBtn(record.id); // ---- Uygulamanın kurulum çağrısı ---- if (app.onLaunch) { try { app.onLaunch(record, body); } catch (err) { console.error('[WM] onLaunch hatası:', err); body.innerHTML = '
Hata: ' + escapeHtml(err.message) + '
'; } } // ---- onReady çağrısı ---- if (typeof opts.onReady === 'function') { opts.onReady(record, body); } window.Taskbar && window.Taskbar.render(); focusWindow(record.id); return { record, bodyEl: body, win }; } /* ------------ Saydam yardımcı: DOM'a sağlam pencere dış IP arayüzü ------------ createWindow genel (global) fonksiyon olarak dönecek, alt-scriptler kullanır. Diğer modüller window.createWindow ≠ çalışmasın diye: */ window.createWindow = createWindow; /* ---- Odak (z-sırası) Yönetimi ---- */ function focusWindow(id) { const rec = getWindow(id); if (!rec) return; // Zaten aktifse ama minimize ise restore olur if (rec.minimized) return; // taskbar üzerinden restore edilir // Aktif pencere sınıfını temizle Wm.windows.forEach(w => { w.el.classList.remove('active'); w.taskBtn && w.taskBtn.classList.remove('active'); }); rec.el.classList.add('active'); rec.el.style.zIndex = ++Wm.zCounter; Wm.activeWin = rec; if (recordBtnById(id)) recordBtnById(id).classList.add('active'); rec.taskBtn && rec.taskBtn.classList.add('active'); window.Taskbar && window.Taskbar.render(); // Pencereyi minimum boyutun üzerinde tutmaz, minimize restore if (rec.minimized && rec.__doRestore) topLevelRestore(rec); } function topLevelRestore(rec) { rec.el.classList.remove('minimized'); rec.minimized = false; rec.el.style.display = 'flex'; } /* ------------ Küçült ------------ */ function minimizeWindow(id) { const rec = getWindow(id); if (!rec) return; rec.el.classList.add('minimized'); rec.minimized = true; rec.el.classList.remove('active'); rec.taskBtn && rec.taskBtn.classList.remove('active'); // Aktifliği bırak if (Wm.activeWin && Wm.activeWin.id === id) { Wm.activeWin = null; // Aktifliği diğer pencereye aktar const others = Wm.windows.filter(w => !w.minimized); if (others.length) focusWindow(others[others.length - 1].id); } window.Taskbar && window.Taskbar.render(); } /* ------------ Geri getir (restore) ------------ */ function restoreWindow(id) { const rec = getWindow(id); if (!rec) return; rec.el.classList.remove('minimized'); rec.minimized = false; rec.el.classList.add('active'); rec.el.style.zIndex = ++Wm.zCounter; Wm.activeWin = rec; focusWindow(id); window.Taskbar && window.Taskbar.render(); } /* ------------ Kapat ------------ */ function closeWindow(id) { const rec = getWindow(id); if (!rec) return; const idx = Wm.windows.indexOf(rec); if (idx >= 0) Wm.windows.splice(idx, 1); // Görev çubuğu butonunu kaldır if (window.Taskbar) { window.Taskbar.removeTaskButton(id); } if (rec.el.parentNode) rec.el.parentNode.removeChild(rec.el); if (Wm.activeWin && Wm.activeWin.id === id) { Wm.activeWin = null; const others = Wm.windows.filter(w => !w.minimized); if (others.length) focusWindow(others[others.length - 1].id); } window.Taskbar && window.Taskbar.render(); } /* ------------ Yardımcı fonksiyonlar ------------ */ function getWindow(id) { return Wm.windows.find(w => w.id === id) || null; } function recordBtnById(id) { const rec = getWindow(id); return rec && rec.taskBtn; } function escapeHtml(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } /* ------------ Fareyle sürükleme (drag) ------------ */ function setupDrag(rec) { const titlebar = rec.titlebar; let isDragging = false; let startX, startY, origX, origY, origPointerX, origPointerY; titlebar.addEventListener('mousedown', (e) => { // Sol tuş; başlık butonlarına tıklanmışsa sürükleme başlatma if (e.button !== 0) return; if (e.target.closest('.win-btns')) return; isDragging = true; const rect = rec.el.getBoundingClientRect(); startX = rect.left; startY = rect.top; origPointerX = e.clientX; origPointerY = e.clientY; rec.el.style.transition = 'none'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const dx = e.clientX - origPointerX; const dy = e.clientY - origPointerY; let nx = startX + dx; let ny = startY + dy; // Ekran sınırları içinde tut (kısmen taşmasın) const vw = window.innerWidth; const vh = window.innerHeight - 50; nx = Math.min(nx, vw - 60); ny = Math.min(ny, vh - 30); nx = Math.max(nx, -rec.el.offsetWidth + 80); ny = Math.max(ny, 0); rec.el.style.left = nx + 'px'; rec.el.style.top = ny + 'px'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; rec.el.style.transition = ''; } }); } /* ----- Pencere sırasını gösteren API (debug/taskbar) ----- */ window.Wm = Wm;