Files
os-gui/script.js
usernames122 55e1616e1b first commit
2025-08-09 05:25:35 +02:00

303 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// WINDOW MANAGER
const desktop = document.getElementById('desktop');
const bglayer = document.getElementById('background-layer');
const newWindowBtn = document.getElementById('new-window-btn');
let zIndexCounter = 1;
let focusedWindow = null;
let windows = {};
let windowHooks = [];
function uniqueid() {
// always start with a letter (for DOM friendlyness)
var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode = Math.floor((Math.random() * 42) + 48);
if (ascicode < 58 || ascicode > 64) {
// exclude all chars between : (58) and @ (64)
idstr += String.fromCharCode(ascicode);
}
} while (idstr.length < 32);
return (idstr);
}
function fadeOut(fadeTarget, speed, callback) {
let eee = 0;
var fadeEffect = setInterval(function () {
if (!fadeTarget.style.opacity) {
fadeTarget.style.opacity = 1;
}
fadeTarget.style.transform = `rotate3d(1, 0, 0, ${eee}deg)`;
if (fadeTarget.style.opacity > 0) {
fadeTarget.style.opacity -= 0.1;
eee += 10;
} else {
clearInterval(fadeEffect);
callback();
}
}, speed);
}
function killWind(id) {
const windowDiv = windows[id]?.windElems?.windowDiv;
if (typeof (windowDiv) == "undefined") {
console.warn("Attempted to close undefined window..");
return;
};
fadeOut(windowDiv, 20, function () {
windowDiv.remove();
delete windows[id];
windowHooks.forEach(element => {
element({ event: "windowDestroy", id: id })
});
});
}
function focusWindow(id) {
const windData = windows[id];
const windowDiv = windData?.windElems?.windowDiv;
if (typeof (windData) == "undefined") {
console.warn("Attempted to focus undefined window..");
return;
};
windowDiv.style.zIndex = zIndexCounter++;
if (windData.layer == "background") windowDiv.style.zIndex = windowDiv.style.zIndex * -1;
windowHooks.forEach(element => {
element({ event: "windowFocused", id: id })
});
Object.keys(windows).forEach(key => {
const element = windows[key];
if (element.alwaysOnTop) {
element.windElems.windowDiv.style.zIndex = zIndexCounter + 5;
}
});
focusedWindow = id;
}
function createWindow(url, settings) {
const windID = uniqueid();
const name = settings.name;
const headerless = settings.headerless || false;
const resizable = settings.resizable !== undefined ? settings.resizable : true;
const shouldIndent = settings.indent !== undefined ? settings.indent : true;
const layer = settings.layer || "foreground";
const windowDiv = document.createElement('div');
windowDiv.classList.add('window');
windowDiv.style.top = '50px';
if (shouldIndent) windowDiv.style.left = '50px';
windowDiv.style.zIndex = zIndexCounter++;
windowDiv.id = windID;
// Header
const header = document.createElement('div');
header.classList.add('window-header');
if (headerless) header.classList.add("window-hidden");
const title = document.createElement('div');
title.classList.add('window-title');
title.textContent = name || url;
const closeBtn = document.createElement('button');
closeBtn.classList.add('window-close-btn');
closeBtn.textContent = '×';
header.appendChild(title);
header.appendChild(closeBtn);
windowDiv.appendChild(header);
// iframe
const iframe = document.createElement('iframe');
iframe.classList.add('window-iframe');
iframe.src = url;
windowDiv.appendChild(iframe);
iframe.onload = function () {
try {
iframe.contentDocument.oncontextmenu = function () {
return false;
};
} catch { };
};
// Add resize handle if resizable
let resizeHandle = null;
if (resizable) {
resizeHandle = document.createElement('div');
resizeHandle.style.width = '16px';
resizeHandle.style.height = '16px';
resizeHandle.style.position = 'absolute';
resizeHandle.style.right = '0';
resizeHandle.style.bottom = '0';
resizeHandle.style.cursor = 'se-resize';
// Optional: make it visible if you want
// resizeHandle.style.background = 'rgba(0,0,0,0.2)';
windowDiv.appendChild(resizeHandle);
}
if (layer == "foreground") desktop.appendChild(windowDiv);
else bglayer.appendChild(windowDiv);
// Dragging functionality
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - windowDiv.getBoundingClientRect().left;
offsetY = e.clientY - windowDiv.getBoundingClientRect().top;
focusWindow(windID);
});
// Resizing variables
let isResizing = false;
let startWidth, startHeight, startX, startY;
window.addEventListener('mouseup', () => {
isDragging = false;
isResizing = false;
});
window.addEventListener('mousemove', (e) => {
if (isDragging) {
let newX = e.clientX - offsetX;
let newY = e.clientY - offsetY;
transformWindow(windID, null, null, newX, newY);
} else if (isResizing) {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
const newWidth = startWidth + dx;
const newHeight = startHeight + dy;
transformWindow(windID, newWidth, newHeight);
}
});
// Resize handle mousedown
if (resizeHandle) {
resizeHandle.addEventListener('mousedown', (e) => {
e.stopPropagation();
isResizing = true;
startWidth = windowDiv.offsetWidth;
startHeight = windowDiv.offsetHeight;
startX = e.clientX;
startY = e.clientY;
focusWindow(windID);
});
}
// Focus window on click
windowDiv.addEventListener('mousedown', () => {
focusWindow(windID);
});
// Close window
closeBtn.addEventListener('click', () => {
killWind(windID);
});
const windObj = {
iframe: iframe,
windElems: {
closeBtn: closeBtn,
windowDiv: windowDiv,
header: header,
title: title,
resizeHandle: resizeHandle
},
windID: windID,
minSizeX: 250,
minSizeY: 150,
alwaysOnTop: false,
layer: layer,
hideInTaskbar: layer == "background"
};
windows[windID] = windObj;
windowHooks.forEach(element => {
element({ event: "windowCreate", id: windID, window: windObj })
});
return windObj;
}
function transformWindow(id, sizex, sizey, posx, posy, ignoreRects) {
const windData = windows[id];
const windowDiv = windData?.windElems?.windowDiv;
if (typeof (windData) == "undefined") {
console.warn("Attempted to transform undefined window..");
return;
};
if (sizex) windowDiv.style.width = Math.max(windData.minSizeX, sizex) + 'px';
if (sizey) windowDiv.style.height = Math.max(windData.minSizeY, sizey) + 'px';
const desktopRect = desktop.getBoundingClientRect();
const winRect = windowDiv.getBoundingClientRect();
if (posx) {
const newX = ignoreRects ? posx : Math.max(desktopRect.left, Math.min(posx, desktopRect.right - winRect.width));
windowDiv.style.left = newX - desktopRect.left + 'px';
}
if (posy) {
const newY = ignoreRects ? posy : Math.max(desktopRect.top, Math.min(posy, desktopRect.bottom - winRect.height));
windowDiv.style.top = newY - desktopRect.top + 'px';
}
}
function getTransform(id) {
const windData = windows[id];
const windowDiv = windData?.windElems?.windowDiv;
if (typeof windData === "undefined" || !windowDiv) {
console.warn("Attempted to transform undefined window..");
return;
}
const desktopRect = desktop.getBoundingClientRect();
// Extract numeric values from style or fallback to computed styles
const style = windowDiv.style;
// Helper to parse px values safely
function parsePx(val) {
if (!val) return 0;
return Math.floor(parseFloat(val));
}
// Use inline style if present, otherwise computed style
let topStr = style.top || window.getComputedStyle(windowDiv).top;
let leftStr = style.left || window.getComputedStyle(windowDiv).left;
let widthStr = style.width || window.getComputedStyle(windowDiv).width;
let heightStr = style.height || window.getComputedStyle(windowDiv).height;
// Parse numbers from strings like "50px"
const posy = parsePx(topStr);
const posx = parsePx(leftStr);
const sizex = parsePx(widthStr);
const sizey = parsePx(heightStr);
return {
x: posx,
y: posy,
sizex: sizex,
sizey: sizey
};
}
function updateWindowMin(id) {
const windData = windows[id];
if (typeof (windData) == "undefined") {
console.warn("Attempted to update undefined window..");
return;
};
const transform = getTransform(id);
transformWindow(id,transform.sizex,transform.sizey,transform.posx,transform.posy);
}