Implemented Backend WindowAPI
This commit is contained in:
39
Frontend/package-lock.json
generated
39
Frontend/package-lock.json
generated
@@ -20,7 +20,7 @@
|
||||
"@angular/router": "~13.1.0",
|
||||
"rxjs": "~7.4.0",
|
||||
"tslib": "^2.3.0",
|
||||
"webdesktop_windowapi": "^1.0.4",
|
||||
"webdesktop_windowapi": "^1.0.9",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -11129,24 +11129,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdesktop_windowapi": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/webdesktop_windowapi/-/webdesktop_windowapi-1.0.4.tgz",
|
||||
"integrity": "sha512-UmL3DsTz2Tct0EI4llDOT5cn76wRCq9HJPL92EwQQykscv/4N0PXucdVf3eRLfxg7jtWNTDajW/taCx1l6zbCg==",
|
||||
"dependencies": {
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/webdesktop_windowapi/node_modules/typescript": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/webdesktop_windowapi/-/webdesktop_windowapi-1.0.9.tgz",
|
||||
"integrity": "sha512-q84Auqs9HCjIi1T+Ny8sdCXQq00b4JZqVpiAhMS5vKpQmOMDtkarsgUgSiU0VRhtnAKojXsDENsqNtCXTYbhGw=="
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.65.0",
|
||||
@@ -19728,19 +19713,9 @@
|
||||
}
|
||||
},
|
||||
"webdesktop_windowapi": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/webdesktop_windowapi/-/webdesktop_windowapi-1.0.4.tgz",
|
||||
"integrity": "sha512-UmL3DsTz2Tct0EI4llDOT5cn76wRCq9HJPL92EwQQykscv/4N0PXucdVf3eRLfxg7jtWNTDajW/taCx1l6zbCg==",
|
||||
"requires": {
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ=="
|
||||
}
|
||||
}
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/webdesktop_windowapi/-/webdesktop_windowapi-1.0.9.tgz",
|
||||
"integrity": "sha512-q84Auqs9HCjIi1T+Ny8sdCXQq00b4JZqVpiAhMS5vKpQmOMDtkarsgUgSiU0VRhtnAKojXsDENsqNtCXTYbhGw=="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.65.0",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@angular/router": "~13.1.0",
|
||||
"rxjs": "~7.4.0",
|
||||
"tslib": "^2.3.0",
|
||||
"webdesktop_windowapi": "^1.0.4",
|
||||
"webdesktop_windowapi": "^1.0.9",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="header-button">
|
||||
<mat-icon class="minimize" (click)="toggleMinimized()">minimize</mat-icon>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
<div class="header-button" *ngIf="resizable">
|
||||
<mat-icon (click)="toggleMaximized()">{{resizeIcon}}</mat-icon>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||
import {DesktopComponent, ProgramArgs} from "../../sites/desktop/desktop.component";
|
||||
import {TaskbarIcon} from "../../sites/desktop/taskbar-icon/taskbar-icon.component";
|
||||
import {Package} from "webdesktop_windowapi/dist/helper/Package";
|
||||
import {WindowEvent} from "webdesktop_windowapi/dist/helper/EventData";
|
||||
|
||||
@Component({
|
||||
selector: 'app-window-wrapper',
|
||||
@@ -18,17 +20,26 @@ export class WindowWrapper {
|
||||
public title: string;
|
||||
public focused: boolean;
|
||||
public contentLoaded: boolean = false;
|
||||
public draggable: boolean = true;
|
||||
public resizable: boolean = true;
|
||||
public asPopup: boolean;
|
||||
public args: string[];
|
||||
|
||||
public resizeIcon: string = "fullscreen";
|
||||
public maximized: boolean = false;
|
||||
private lastPos: { left: string, top: string, width: string, height: string };
|
||||
private readonly messageHandler: (event: MessageEvent) => void;
|
||||
|
||||
constructor(private object: ElementRef) {
|
||||
this.uuid = DesktopComponent.instance.generateWindowUUID();
|
||||
this.messageHandler = this.onMessage.bind(this);
|
||||
window.addEventListener('message', this.messageHandler);
|
||||
}
|
||||
|
||||
public initialize(taskbar: TaskbarIcon) {
|
||||
public initialize(taskbar: TaskbarIcon, args: string[], asPopup: boolean) {
|
||||
this.title = this.program.name;
|
||||
this.args = args;
|
||||
this.asPopup = asPopup;
|
||||
this.dragHandler = new DragHandler(this.wrapper.nativeElement, this);
|
||||
this.resizeHandler = new ResizeHandler(this.wrapper.nativeElement, this);
|
||||
this.taskbar = taskbar;
|
||||
@@ -37,6 +48,8 @@ export class WindowWrapper {
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.dispatchEvent({type: "close", data: undefined});
|
||||
window.removeEventListener('message', this.messageHandler);
|
||||
this.object.nativeElement.parentElement.removeChild(this.object.nativeElement);
|
||||
this.taskbar.onClose(this);
|
||||
}
|
||||
@@ -104,12 +117,21 @@ export class WindowWrapper {
|
||||
this.focused = false;
|
||||
}
|
||||
|
||||
public receiveMessageFromWindow(sender: WindowWrapper, data: any) {
|
||||
console.log(sender, data);
|
||||
}
|
||||
|
||||
public applyContentListeners(content: HTMLIFrameElement) {
|
||||
content.contentDocument.addEventListener('mousemove', this.onMove.bind(this));
|
||||
content.contentDocument.addEventListener('mousedown', this.focus.bind(this));
|
||||
content.contentDocument.addEventListener('mouseup', this.resizeHandler?.windowResizeStop.bind(this.resizeHandler));
|
||||
/*content.contentWindow.addEventListener('mousemove', this.onMove.bind(this));
|
||||
content.contentWindow.addEventListener('mousedown', this.focus.bind(this));
|
||||
content.contentWindow.addEventListener('mouseup', this.resizeHandler?.windowResizeStop.bind(this.resizeHandler));*/
|
||||
|
||||
this.contentLoaded = true;
|
||||
|
||||
if (this.asPopup)
|
||||
this.dispatchEvent({type: "openAsPopup", data: this.args});
|
||||
else
|
||||
this.dispatchEvent({type: "open", data: this.args});
|
||||
}
|
||||
|
||||
public onMove(event: MouseEvent) {
|
||||
@@ -117,6 +139,140 @@ export class WindowWrapper {
|
||||
this.resizeHandler?.windowResize(event);
|
||||
}
|
||||
|
||||
private onMessage(event: MessageEvent) {
|
||||
const iframe = this.content.nativeElement as HTMLIFrameElement;
|
||||
if (event.source != iframe.contentWindow) return;
|
||||
|
||||
const data = event.data as Package;
|
||||
if (data.method == 'get') {
|
||||
const pkg: Package = {method: "get", content: this.handleGet(data)};
|
||||
iframe.contentWindow.postMessage(pkg, event.origin);
|
||||
}
|
||||
else if (data.method == 'set') {
|
||||
this.handleSet(data);
|
||||
}
|
||||
else if (data.method == 'action') {
|
||||
const pkg: Package = {method: "action", content: this.handleAction(data)};
|
||||
|
||||
if (data.action != 'closeSelf' && (data.action == 'closeWindow' && data.content.uuid == this.uuid))
|
||||
iframe.contentWindow.postMessage(pkg, event.origin);
|
||||
}
|
||||
}
|
||||
|
||||
private handleGet(data: Package): any {
|
||||
const element: HTMLElement = this.wrapper.nativeElement;
|
||||
switch (data.variable) {
|
||||
case "title":
|
||||
return this.title;
|
||||
|
||||
case "maximized":
|
||||
return this.maximized;
|
||||
|
||||
case "minimized":
|
||||
return this.object.nativeElement.style.display == 'none';
|
||||
|
||||
case "draggable":
|
||||
return this.draggable;
|
||||
|
||||
case "resizable":
|
||||
return this.maximized;
|
||||
|
||||
case "position":
|
||||
const x = element.offsetLeft;
|
||||
const y = element.offsetTop;
|
||||
return {x, y};
|
||||
|
||||
case "size":
|
||||
const width = element.offsetWidth;
|
||||
const height = element.offsetHeight;
|
||||
return {width, height};
|
||||
|
||||
case "uuid":
|
||||
return this.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
private handleSet(data: Package) {
|
||||
const element: HTMLElement = this.wrapper.nativeElement;
|
||||
switch (data.variable) {
|
||||
case "title":
|
||||
this.title = data.content;
|
||||
break;
|
||||
|
||||
case "maximized":
|
||||
if (this.maximized != data.content)
|
||||
this.toggleMaximized();
|
||||
break;
|
||||
|
||||
case "minimized":
|
||||
if ((this.object.nativeElement.style.display == 'none') != data.content)
|
||||
this.toggleMinimized();
|
||||
break;
|
||||
|
||||
case "draggable":
|
||||
this.draggable = data.content;
|
||||
break;
|
||||
|
||||
case "resizable":
|
||||
this.resizable = data.content;
|
||||
break;
|
||||
|
||||
case "position":
|
||||
element.style.left = data.content.x + "px";
|
||||
element.style.top = data.content.y + "px";
|
||||
|
||||
const x = data.content.x, y = data.content.y;
|
||||
this.dispatchEvent({type: "move", data: {fromDrag: false, x, y}});
|
||||
break;
|
||||
|
||||
case "size":
|
||||
element.style.width = data.content.width + "px";
|
||||
element.style.height = data.content.height + "px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private handleAction(data: Package): any {
|
||||
switch (data.action) {
|
||||
case "openWindow":
|
||||
return DesktopComponent.instance.openProgram(data.content.identifier, data.content.args, data.content.asPopup);
|
||||
|
||||
case "closeWindow":
|
||||
const window = DesktopComponent.instance.getWindow(data.content.uuid);
|
||||
if (window == undefined) return false;
|
||||
window.close();
|
||||
return true;
|
||||
|
||||
case "messageWindow":
|
||||
const target = DesktopComponent.instance.getWindow(data.content.uuid);
|
||||
if (target == undefined) return false;
|
||||
target.receiveMessageFromWindow(this, data.content.data);
|
||||
return true;
|
||||
|
||||
case "closeSelf":
|
||||
this.close();
|
||||
return;
|
||||
|
||||
case "focus":
|
||||
this.focus();
|
||||
return;
|
||||
|
||||
case "notification":
|
||||
DesktopComponent.instance.sendNotification(data.content);
|
||||
return;
|
||||
|
||||
case "popup":
|
||||
console.log("POPUP");
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public dispatchEvent(event: WindowEvent) {
|
||||
const iframe = this.content.nativeElement as HTMLIFrameElement;
|
||||
const pkg: Package = {method: "event", event: event.type, content: event};
|
||||
iframe.contentWindow.postMessage(pkg, new URL(iframe.src).origin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DragHandler {
|
||||
@@ -125,16 +281,18 @@ class DragHandler {
|
||||
public dragging: boolean;
|
||||
private origTransitions: string;
|
||||
|
||||
public constructor(private object: HTMLElement, private wrapper: WindowWrapper) {
|
||||
}
|
||||
public constructor(private object: HTMLElement, private wrapper: WindowWrapper) {}
|
||||
|
||||
public windowDrag(event: MouseEvent): void {
|
||||
if (!this.wrapper.draggable) return;
|
||||
if (!this.dragging) return;
|
||||
if (!this.wrapper.contentLoaded) return;
|
||||
const x = event.clientX - this.offsetX;
|
||||
const y = event.clientY - this.offsetY;
|
||||
this.object.style.left = x + 'px';
|
||||
this.object.style.top = y + 'px';
|
||||
|
||||
this.wrapper.dispatchEvent({type: "move", data: {fromDrag: true, x, y}});
|
||||
}
|
||||
|
||||
public windowDragStart(event: MouseEvent): void {
|
||||
@@ -168,8 +326,7 @@ class ResizeHandler {
|
||||
public resizing: boolean = false;
|
||||
private lastResizeManager: string;
|
||||
|
||||
public constructor(private object: HTMLElement, private wrapper: WindowWrapper) {
|
||||
}
|
||||
public constructor(private object: HTMLElement, private wrapper: WindowWrapper) {}
|
||||
|
||||
public windowResizeStart(event: MouseEvent): void {
|
||||
if (this.wrapper.maximized) return;
|
||||
@@ -191,6 +348,7 @@ class ResizeHandler {
|
||||
}
|
||||
|
||||
public windowResize(event: MouseEvent) {
|
||||
if (!this.wrapper.resizable) return;
|
||||
if (!this.wrapper.focused) return;
|
||||
if (this.wrapper.maximized) return;
|
||||
|
||||
@@ -245,6 +403,8 @@ class ResizeHandler {
|
||||
if (newDimensions.y) this.object.style.top = newDimensions.y + 'px';
|
||||
if (newDimensions.width) this.object.style.width = newDimensions.width + 'px';
|
||||
if (newDimensions.height) this.object.style.height = newDimensions.height + 'px';
|
||||
|
||||
this.wrapper.dispatchEvent({type: "resize", data: newDimensions});
|
||||
}
|
||||
|
||||
private isHoverBorder(event: MouseEvent): string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {ChangeDetectorRef, Component, OnInit, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {TaskbarIcon} from "./taskbar-icon/taskbar-icon.component";
|
||||
import {WindowWrapper} from "../../components/window-wrapper/window-wrapper.component";
|
||||
import {Notification} from "webdesktop_windowapi/dist/helper/Notification";
|
||||
|
||||
export interface IconType {
|
||||
uuid: number;
|
||||
@@ -20,10 +21,10 @@ export interface ProgramArgs {
|
||||
}
|
||||
|
||||
export const programs: {[programUUID: string]: ProgramArgs} = {
|
||||
['defender']: {
|
||||
identifier: 'defender',
|
||||
name: 'Windows Defender',
|
||||
handlerUrl: 'http://localhost:4200/',
|
||||
['tester']: {
|
||||
identifier: 'tester',
|
||||
name: 'WindowAPI Tester',
|
||||
handlerUrl: 'http://localhost:8080/',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +57,18 @@ export class DesktopComponent implements OnInit {
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
|
||||
setTimeout(() => {
|
||||
this.addTaskbarIcon(programs['defender']);
|
||||
this.addTaskbarIcon(programs['tester']);
|
||||
});
|
||||
}
|
||||
|
||||
public openProgram(programUUID: string) {
|
||||
public openProgram(programUUID: string, args?: string[], asPopup?: boolean): number {
|
||||
const program = programs[programUUID];
|
||||
const exists = this.getTaskbarIcon(programUUID) != undefined;
|
||||
|
||||
if (!exists)
|
||||
this.addTaskbarIcon(program, true);
|
||||
|
||||
this.getTaskbarIcon(programUUID).icon.openProgram();
|
||||
return this.getTaskbarIcon(programUUID).icon.openProgram(args, asPopup);
|
||||
}
|
||||
|
||||
public addTaskbarIcon(program: ProgramArgs, removeOnClose: boolean = false, index?: number) {
|
||||
@@ -132,6 +133,10 @@ export class DesktopComponent implements OnInit {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public sendNotification(notification: Notification) {
|
||||
console.log(notification);
|
||||
}
|
||||
|
||||
public mouseMove(event: MouseEvent) {
|
||||
DesktopComponent.focusedWindow?.onMove(event);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,15 @@ export class TaskbarIcon {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public openProgram() {
|
||||
public openProgram(args?: string[], asPopup: boolean = false): number {
|
||||
const window = DesktopComponent.windowContainer.createComponent(WindowWrapper);
|
||||
window.instance.program = this.type.program;
|
||||
DesktopComponent.instance.cdr.detectChanges();
|
||||
window.instance.initialize(this);
|
||||
window.instance.initialize(this, args || [], asPopup);
|
||||
|
||||
this.windows.push(window.instance);
|
||||
this.setIndicator('wide');
|
||||
return window.instance.uuid;
|
||||
}
|
||||
|
||||
public onTaskbarClick(event: MouseEvent) {
|
||||
|
||||
Reference in New Issue
Block a user