DK2/public/assets/scripts/WindowManager.js
2024-11-13 18:53:05 -08:00

176 lines
4.3 KiB
JavaScript

/*
The WindowManager is responsible for creation, destruction, modification and tracking of game UI windows.
*/
class WindowManager
{
constructor(container)
{
this.windows = {}
this.container = container
}
updateWindow(id, content, title = '')
{
if (id in this.windows) {
let w = this.windows[id]
w.querySelector('header .title').innerHTML = title
w.querySelector('.body').innerHTML = content
this.bringToFront(w)
return
}
let w = this.createWindow(id, content, title)
this.bringToFront(w)
}
createWindow(id, content, title = '')
{
// create window
let w = document.createElement('div')
w.id = `window-${id}`
w.classList.add('window')
// create header
let h = document.createElement('header')
w.appendChild(h)
// create header title
let ht = document.createElement('span')
ht.classList.add('title')
ht.innerHTML = title
h.appendChild(ht)
// create close button
ht.insertAdjacentHTML('afterend', '<a class="close"><img src="/assets/img/ui/icons/bullet_red.png"></a>')
h.querySelector('a.close').addEventListener('click', () => {
this.windows[id].remove()
delete this.windows[id]
})
// create body
let b = document.createElement('div')
b.classList.add('body')
b.innerHTML = content
w.appendChild(b)
// track window and add it to the container
this.makeWindowDraggable(w, this.container)
this.windows[id] = w
this.container.appendChild(w)
return w
}
makeWindowDraggable(w, c)
{
const header = w.querySelector('header');
if (!header) return;
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
header.addEventListener('mousedown', startDragging);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDragging);
w.addEventListener('mousedown', () => this.bringToFront(w));
function startDragging(e)
{
isDragging = true;
initialX = e.clientX - w.offsetLeft;
initialY = e.clientY - w.offsetTop;
}
function drag(e)
{
if (!isDragging) return;
e.preventDefault();
// Calculate new position
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
// Get viewport dimensions
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Get window dimensions
const windowRect = w.getBoundingClientRect();
// Constrain to viewport bounds
// Left edge
currentX = Math.max(0, currentX);
// Right edge
currentX = Math.min(viewportWidth - windowRect.width, currentX);
// Top edge
currentY = Math.max(0, currentY);
// Bottom edge
currentY = Math.min(viewportHeight - windowRect.height, currentY);
// Apply the constrained position
w.style.left = currentX + 'px';
w.style.top = currentY + 'px';
}
function stopDragging()
{
isDragging = false;
}
// Handle window resize
window.addEventListener('resize', () => {
// Get current position and dimensions
const rect = w.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Adjust position if window is outside viewport after resize
let newX = parseInt(w.style.left);
let newY = parseInt(w.style.top);
// Constrain to new viewport bounds
newX = Math.min(Math.max(0, newX), viewportWidth - rect.width);
newY = Math.min(Math.max(0, newY), viewportHeight - rect.height);
w.style.left = newX + 'px';
w.style.top = newY + 'px';
});
}
normalizeZIndices()
{
const array = Array.from(this.windows);
array.sort((a, b) => (parseInt(a.style.zIndex) || 0) - (parseInt(b.style.zIndex) || 0));
// Reassign z-indices starting from 1
array.forEach((win, index) => {
win.style.zIndex = index + 1;
});
return array.length + 1; // Return next available z-index
}
bringToFront(windowElement)
{
const currentMax = Math.max(...Object.values(this.windows)
.map(w => parseInt(w.style.zIndex) || 0));
if (parseInt(windowElement.style.zIndex) >= currentMax) {
return
}
// If z-index is getting too high, normalize all z-indices
if (currentMax > 10000) {
const newZ = this.normalizeZIndices();
windowElement.style.zIndex = newZ;
} else {
windowElement.style.zIndex = currentMax + 1;
}
}
}