var orderIndex = 1000; export class WindowController { win = null; titlebar = null; canvas = null; resizers = null; btnClose = null; btnMaximize = null; isSnapped = false; isMaximized = false; isDragging = false; dragType = null; currentResizer = null; savedPosition = null; savedBeforeMaximize = null; order = orderIndex++; startX = 0; startY = 0; startLeft = 100; startTop = 100; startWidth = 500; startHeight = 500; width = this.startWidth; height = this.startHeight; constructor() { } setElement( element ) { this.canvas = element; console.log("set element",element); } create( element ) { var windowFrame = document.createElement( "div" ); windowFrame.id = "win" windowFrame.style.width = this.startWidth + "px"; windowFrame.style.height = this.startHeight + "px"; windowFrame.style.left = this.startLeft + "px"; windowFrame.style.top = this.startTop + "px"; // Title bar var titleBar = document.createElement( "div" ); titleBar.className = "titlebar"; titleBar.textContent = "Source Code Graph"; var buttons = document.createElement( "div" ); buttons.className = "buttons"; var btnMaximize = document.createElement( "button" ); btnMaximize.id = "btnMaximize"; btnMaximize.title = "Maximize"; btnMaximize.textContent = "m"; var btnClose = document.createElement( "button" ); btnClose.id = "btnClose"; btnClose.title = "Close"; btnClose.textContent = "×"; buttons.appendChild( btnMaximize ); buttons.appendChild( btnClose ); titleBar.appendChild( buttons ); windowFrame.appendChild( titleBar ); // Content var content = document.createElement( "div" ); content.className = "content"; console.log(this.canvas); content.appendChild( this.canvas ); windowFrame.appendChild( content ); // Resizers var resizerClasses = [ "top-left", "top", "top-right", "right", "bottom-right", "bottom", "bottom-left", "left" ]; this.resizers = new Array(); for (var i = 0; i < resizerClasses.length; i++) { var resizer = document.createElement( "div" ); resizer.className = "resizer " + resizerClasses[i]; windowFrame.appendChild( resizer ); this.resizers.push(resizer) } this.win = windowFrame; this.titlebar = titleBar; this.btnClose = btnClose; this.btnMaximize = btnMaximize; this.titlebar.addEventListener("mousedown", this.onTitlebarMouseDown.bind(this)); this.resizers.forEach(this.bindResizer.bind(this)); this.btnClose.addEventListener("click", this.onCloseClick.bind(this)); this.btnMaximize.addEventListener("click", this.onMaximizeClick.bind(this)); document.body.appendChild( windowFrame ); this.win.addEventListener("mousedown", this.reOrder.bind( this ) ); } setup() { this.create(); } setGraphExplorer( graphExplorer ) { this.graphExplorer = graphExplorer; } saveWindowState() { this.savedPosition = { left: this.win.offsetLeft, top: this.win.offsetTop, width: this.win.offsetWidth, height: this.win.offsetHeight }; } hide() { this.win.style.display = "none" } show() { this.win.style.display = "flex" } restoreWindowState() { if (!this.savedPosition) return; this.win.style.left = this.savedPosition.left + "px"; this.win.style.top = this.savedPosition.top + "px"; this.win.style.width = this.savedPosition.width + "px"; this.win.style.height = this.savedPosition.height + "px"; this.savedPosition = null; this.isSnapped = false; } resizeCanvas() { const style = getComputedStyle(this.win); const width = parseInt(style.width); const height = parseInt(style.height); //this.canvas.width = width; //this.canvas.height = height; //this.canvas.style.width = width + "px"; //this.canvas.style.height = height + "px"; //console.log(this.graphExplorer); if(this.graphExplorer) this.graphExplorer.onResize( width, height ); //this.draw(); } snapWindow(mouseX, mouseY) { if (this.isMaximized) return; const snapMargin = 30; const vw = window.innerWidth; const vh = window.innerHeight; const nearLeft = mouseX <= snapMargin; const nearRight = mouseX >= vw - snapMargin; const nearTop = mouseY <= snapMargin; const nearBottom = mouseY >= vh - snapMargin; if (!this.isSnapped && (nearLeft || nearRight || nearTop || nearBottom)) { this.saveWindowState(); this.isSnapped = true; } else if (!nearLeft && !nearRight && !nearTop && !nearBottom && this.isSnapped) { this.restoreWindowState(); return; } if (!this.isSnapped) return; if (nearTop && nearLeft) { this.setWindowRect(0, 0, vw / 2, vh / 2); } else if (nearTop && nearRight) { this.setWindowRect(vw / 2, 0, vw / 2, vh / 2); } else if (nearBottom && nearLeft) { this.setWindowRect(0, vh / 2, vw / 2, vh / 2); } else if (nearBottom && nearRight) { this.setWindowRect(vw / 2, vh / 2, vw / 2, vh / 2); } else if (nearTop) { this.setWindowRect(0, 0, vw, vh / 2); } else if (nearBottom) { this.setWindowRect(0, vh / 2, vw, vh / 2); } else if (nearLeft) { this.setWindowRect(0, 0, vw / 2, vh); } else if (nearRight) { this.setWindowRect(vw / 2, 0, vw / 2, vh); } this.resizeCanvas(); } setWindowRect(left, top, width, height) { this.win.style.left = Math.floor(left) + "px"; this.win.style.top = Math.floor(top) + "px"; this.win.style.width = Math.floor(width) + "px"; this.win.style.height = Math.floor(height) + "px"; } onTitlebarMouseDown(event) { event.preventDefault(); this.dragType = "move"; const offsetX = event.clientX - this.win.offsetLeft; const offsetY = event.clientY - this.win.offsetTop; if (this.isSnapped) { this.restoreWindowState(); this.startX = event.clientX; this.startY = event.clientY; this.startLeft = event.clientX - 100; this.startTop = event.clientY - offsetY; this.win.style.left = this.startLeft + "px"; this.win.style.top = this.startTop + "px"; } else { this.startX = event.clientX; this.startY = event.clientY; this.startLeft = this.win.offsetLeft; this.startTop = this.win.offsetTop; } this.isDragging = true; document.addEventListener("mousemove", this.onDragMove); document.addEventListener("mouseup", this.onDragEnd); } onDragMove = (event) => { event.preventDefault(); if (!this.isDragging) return; const dx = event.clientX - this.startX; const dy = event.clientY - this.startY; if (this.dragType === "move") { let newLeft = this.startLeft + dx; let newTop = this.startTop + dy; newLeft = Math.max(0, Math.min(window.innerWidth - this.win.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(window.innerHeight - this.win.offsetHeight, newTop)); this.win.style.left = newLeft + "px"; this.win.style.top = newTop + "px"; this.snapWindow(event.clientX, event.clientY); } else if (this.dragType === "resize") { let newWidth = this.startWidth; let newHeight = this.startHeight; let newLeft = this.startLeft; let newTop = this.startTop; switch (this.currentResizer) { case "top": newHeight = this.startHeight - dy; newTop = this.startTop + dy; break; case "bottom": newHeight = this.startHeight + dy; break; case "left": newWidth = this.startWidth - dx; newLeft = this.startLeft + dx; break; case "right": newWidth = this.startWidth + dx; break; case "top-left": newWidth = this.startWidth - dx; newLeft = this.startLeft + dx; newHeight = this.startHeight - dy; newTop = this.startTop + dy; break; case "top-right": newWidth = this.startWidth + dx; newHeight = this.startHeight - dy; newTop = this.startTop + dy; break; case "bottom-left": newWidth = this.startWidth - dx; newLeft = this.startLeft + dx; newHeight = this.startHeight + dy; break; case "bottom-right": newWidth = this.startWidth + dx; newHeight = this.startHeight + dy; break; } if (newWidth > 100) { this.win.style.width = newWidth + "px"; this.win.style.left = newLeft + "px"; } if (newHeight > 100) { this.win.style.height = newHeight + "px"; this.win.style.top = newTop + "px"; } } this.resizeCanvas(); }; onDragEnd = () => { this.isDragging = false; this.dragType = null; this.currentResizer = null; document.removeEventListener("mousemove", this.onDragMove); document.removeEventListener("mouseup", this.onDragEnd); }; reOrder() { orderIndex++; this.win.style['z-index'] = orderIndex; console.log("this.win.style['z-index']", this.win.style['z-index']); } onResizerMouseDown(event) { event.preventDefault(); this.startX = event.clientX; this.startY = event.clientY; this.startLeft = this.win.offsetLeft; this.startTop = this.win.offsetTop; this.startWidth = this.win.offsetWidth; this.startHeight = this.win.offsetHeight; this.isDragging = true; this.dragType = "resize"; this.currentResizer = event.target.classList[1]; document.addEventListener("mousemove", this.onDragMove); document.addEventListener("mouseup", this.onDragEnd); } bindResizer(resizerElement) { resizerElement.addEventListener("mousedown", this.onResizerMouseDown.bind(this)); } maximizeWindow() { if (this.isMaximized) return; this.savedBeforeMaximize = { left: this.win.offsetLeft, top: this.win.offsetTop, width: this.win.offsetWidth, height: this.win.offsetHeight }; this.setWindowRect(0, 0, window.innerWidth, window.innerHeight); this.resizeCanvas(); this.isMaximized = true; this.isSnapped = false; this.savedPosition = null; } restoreFromMaximize() { if (!this.isMaximized || !this.savedBeforeMaximize) return; this.setWindowRect( this.savedBeforeMaximize.left, this.savedBeforeMaximize.top, this.savedBeforeMaximize.width, this.savedBeforeMaximize.height ); this.resizeCanvas(); this.isMaximized = false; this.savedBeforeMaximize = null; } onMaximizeClick() { if (this.isMaximized) { this.restoreFromMaximize(); } else { this.maximizeWindow(); } } onCloseClick() { this.win.style.display = "none"; } }