Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for linking windows to state changes of other windows #138

Merged
merged 2 commits into from
Mar 29, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion examples/electron/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ function createWindow() {

desktopJS.ContainerWindow.addListener("window-created", (e) => console.log("Window created - static (ContainerWindow): " + e.windowId + ", " + e.windowName));

snapAssist = new desktopJS.SnapAssistWindowManager(container);
snapAssist = new desktopJS.SnapAssistWindowManager(container,
{
windowStateTracking: desktopJS.WindowStateTracking.Main | desktopJS.WindowStateTracking.Group
});

container.createWindow('http://localhost:8000', { name: "desktopJS", main: true }).then(win => mainWindow = win);

Expand Down
5 changes: 4 additions & 1 deletion examples/web/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ document.addEventListener("DOMContentLoaded", function (event) {
$('[data-toggle="popover"]').popover();

if (container.getCurrentWindow().id === "desktopJS") {
snapAssist = new desktopJS.SnapAssistWindowManager(container);
snapAssist = new desktopJS.SnapAssistWindowManager(container,
{
windowStateTracking: desktopJS.WindowStateTracking.Main | desktopJS.WindowStateTracking.Group
});
}
});

Expand Down
4 changes: 3 additions & 1 deletion src/desktop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { resolveContainer, registerContainer, ContainerRegistration } from "./re
import { Container } from "./container";
import { ContainerNotification } from "./notification";
import { ObjectTransform } from "./propertymapping";
import { ContainerWindow, SnapAssistWindowManager } from "./window";
import { ContainerWindow, WindowStateTracking, GroupWindowManager, SnapAssistWindowManager } from "./window";
import * as Default from "./Default/default";
import * as Electron from "./Electron/electron";
import * as OpenFin from "./OpenFin/openfin";
Expand All @@ -18,5 +18,7 @@ export default class Desktop { //tslint:disable-line
static get Default(): typeof Default { return Default; }
static get Electron(): typeof Electron { return Electron; }
static get OpenFin(): typeof OpenFin { return OpenFin; }
static get WindowStateTracking(): typeof WindowStateTracking { return WindowStateTracking; }
static get GroupWindowManager(): typeof GroupWindowManager { return GroupWindowManager; }
static get SnapAssistWindowManager(): typeof SnapAssistWindowManager { return SnapAssistWindowManager; }
}
98 changes: 79 additions & 19 deletions src/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,82 @@ export class PersistedWindowLayout {

type WindowGroupStatus = {window: ContainerWindow, isGrouped: boolean};

export class SnapAssistWindowManager {
private readonly container: Container;
/** Specifies the tracking behavior for window state linking */
export enum WindowStateTracking {
/** No window state tracking */
None = 0,

/** All windows will follow the window state of the main window */
Main = 1 << 0,

/** All grouped windows will follow the window state of each other */
Group = 1 << 1
}

export class GroupWindowManager {
protected readonly container: Container;
public windowStateTracking: WindowStateTracking = WindowStateTracking.None;

public constructor(container: Container, options? : any) {
this.container = container;

if (options) {
if ("windowStateTracking" in options) {
this.windowStateTracking = options.windowStateTracking;
}
}

this.attach();
}

public attach(win?: ContainerWindow) {
if (win) {
win.addListener((typeof fin !== "undefined") ? <WindowEventType> "minimized" : "minimize", (e) => {
if ((this.windowStateTracking & WindowStateTracking.Main) && this.container.getMainWindow().id === e.sender.id) {
this.container.getAllWindows().then(windows => {
windows.forEach(window => window.innerWindow.minimize());
});
}

if (this.windowStateTracking & WindowStateTracking.Group) {
e.sender.getGroup().then(windows => {
windows.forEach(window => window.innerWindow.minimize());
});
}
});

win.addListener((typeof fin !== "undefined") ? <WindowEventType> "restored" : "restore", (e) => {
if ((this.windowStateTracking & WindowStateTracking.Main) && this.container.getMainWindow().id === e.sender.id) {
this.container.getAllWindows().then(windows => {
windows.forEach(window => window.innerWindow.restore());
});
}

if (this.windowStateTracking & WindowStateTracking.Group) {
e.sender.getGroup().then(windows => {
windows.forEach(window => window.innerWindow.restore());
});
}
});
} else {
// Attach handlers to any new windows that open
ContainerWindow.addListener("window-created", (args) => {
this.container.getWindowById(args.windowId).then(window => {
this.attach(window);
});
});

// Attach handlers to any windows already open
if (this.container) {
this.container.getAllWindows().then(windows => {
windows.forEach(window => this.attach(window));
});
}
}
}
}

export class SnapAssistWindowManager extends GroupWindowManager {
private readonly floater: ContainerWindow;
public autoGrouping: boolean = true;
public snapThreshold: number = 20;
Expand All @@ -269,7 +343,7 @@ export class SnapAssistWindowManager {
protected readonly targetGroup: Map<string, ContainerWindow> = new Map();

public constructor(container: Container, options?: any) {
this.container = container;
super(container, options);

if (options) {
if ("snapThreshold" in options) {
Expand All @@ -284,8 +358,6 @@ export class SnapAssistWindowManager {
this.autoGrouping = options.autoGrouping;
}
}

this.attach();
}

/**
Expand All @@ -310,6 +382,8 @@ export class SnapAssistWindowManager {
}

public attach(win?: ContainerWindow) {
super.attach(win);

if (win) {
this.onAttached(win);

Expand Down Expand Up @@ -367,20 +441,6 @@ export class SnapAssistWindowManager {
});
});
});
} else {
// Attach handlers to any new windows that open
ContainerWindow.addListener("window-created", (args) => {
this.container.getWindowById(args.windowId).then(window => {
this.attach(window);
});
});

// Attach handlers to any windows already open
if (this.container) {
this.container.getAllWindows().then(windows => {
windows.forEach(window => this.attach(window));
});
}
}
}

Expand Down
74 changes: 72 additions & 2 deletions tests/unit/window.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Container } from "../../src/container";
import { ContainerWindow, WindowEventType, WindowEventArgs, SnapAssistWindowManager, Rectangle } from "../../src/window";
import { ContainerWindow, WindowEventType, WindowEventArgs, WindowStateTracking, GroupWindowManager, SnapAssistWindowManager, Rectangle } from "../../src/window";
import { EventArgs, EventEmitter } from "../../src/events";
import { TestContainer, MockMessageBus } from "./container.spec";

class MockWindow extends ContainerWindow {
protected attachListener(eventName: WindowEventType, listener: (event: EventArgs) => void): void {
return;
}
}

describe ("static events", () => {
Expand Down Expand Up @@ -90,6 +93,73 @@ describe("Rectangle", () => {
});
});


describe("GroupWindowManager", () => {
it ("default options", () => {
const mgr = new GroupWindowManager(null);
expect(mgr.windowStateTracking).toEqual(0);
});

it ("overrides read from options", () => {
const mgr = new GroupWindowManager(null, { windowStateTracking: WindowStateTracking.Main });
expect(mgr.windowStateTracking).toEqual(WindowStateTracking.Main);
});

it ("Minimize with Main", () => {
const innerWin = jasmine.createSpyObj("innerwindow", ["minimize", "addListener"]);
const container = jasmine.createSpyObj("container", ["getAllWindows", "getMainWindow"]);
const win = new MockWindow(innerWin);
spyOn(win, "getGroup").and.returnValue(Promise.resolve([win]));
container.getAllWindows.and.returnValue(Promise.resolve([win]));
container.getMainWindow.and.returnValue(win);
const mgr = new GroupWindowManager(container, { windowStateTracking: WindowStateTracking.Main });
mgr.attach(win);
win.emit("minimize", {name: "minimize", sender: win });
expect(container.getAllWindows).toHaveBeenCalledTimes(2); // Once for initial GroupWindowManager ctor
expect(win.getGroup).toHaveBeenCalledTimes(0);
});

it ("Minimize with Group", () => {
const innerWin = jasmine.createSpyObj("innerwindow", ["minimize", "addListener"]);
const container = jasmine.createSpyObj("container", ["getAllWindows", "getMainWindow"]);
const win = new MockWindow(innerWin);
spyOn(win, "getGroup").and.returnValue(Promise.resolve([win]));
container.getAllWindows.and.returnValue(Promise.resolve([win]));
const mgr = new GroupWindowManager(container, { windowStateTracking: WindowStateTracking.Group });
mgr.attach(win);
win.emit("minimize", {name: "minimize", sender: win });
expect(container.getAllWindows).toHaveBeenCalledTimes(1); // Once for initial GroupWindowManager ctor
expect(win.getGroup).toHaveBeenCalled();
});

it ("Restore with Main", () => {
const innerWin = jasmine.createSpyObj("innerwindow", ["restore", "addListener"]);
const container = jasmine.createSpyObj("container", ["getAllWindows", "getMainWindow"]);
const win = new MockWindow(innerWin);
spyOn(win, "getGroup").and.returnValue(Promise.resolve([win]));
container.getAllWindows.and.returnValue(Promise.resolve([win]));
container.getMainWindow.and.returnValue(win);
const mgr = new GroupWindowManager(container, { windowStateTracking: WindowStateTracking.Main });
mgr.attach(win);
win.emit("restore", {name: "restore", sender: win });
expect(container.getAllWindows).toHaveBeenCalledTimes(2); // Once for initial GroupWindowManager ctor
expect(win.getGroup).toHaveBeenCalledTimes(0);
});

it ("Restore with Group", () => {
const innerWin = jasmine.createSpyObj("innerwindow", ["restore", "addListener"]);
const container = jasmine.createSpyObj("container", ["getAllWindows", "getMainWindow"]);
const win = new MockWindow(innerWin);
spyOn(win, "getGroup").and.returnValue(Promise.resolve([win]));
container.getAllWindows.and.returnValue(Promise.resolve([win]));
const mgr = new GroupWindowManager(container, { windowStateTracking: WindowStateTracking.Group });
mgr.attach(win);
win.emit("restore", {name: "restore", sender: win });
expect(container.getAllWindows).toHaveBeenCalledTimes(1); // Once for initial GroupWindowManager ctor
expect(win.getGroup).toHaveBeenCalled();
});
});

describe("SnapAssistWindowManager", () => {
it ("default options", () => {
const mgr = new SnapAssistWindowManager(null);
Expand Down Expand Up @@ -309,4 +379,4 @@ describe("SnapAssistWindowManager", () => {
});
});
});
});
});