windowmanager
Version:
A framework to manage multiple dockable, HTML windows
226 lines (172 loc) • 6.93 kB
JavaScript
import windowmanager from '../global';
import nodeRequire from '../require';
const { BrowserWindow } = nodeRequire('electron');
// TODO: Give the node backend access to windowmanager Window-like functionality.
// This will automatically setup windowmanager on each window if added.
// This is Electron's main process:
const { Vector, BoundingBox } = windowmanager.geometry;
// TODO: Solve event syncing between windows
BrowserWindow.prototype._notifyReady = function () {
for (let other of BrowserWindow.getAllWindows()) {
other.webContents.send('window-create', this.id);
}
};
BrowserWindow.prototype._ensureDockSystem = function () {
// Make sure docked group exists:
if (this._dockedGroup === undefined) {
this._dockedGroup = [this];
this.on('closed', function () {
// Clean up the dock system when this window closes:
this.undock();
});
this.on('maximize', function () {
this.undock(); // TODO: Support changing size when docked.
});
this.on('minimize', function () {
this._dockMinimize();
});
this.on('restore', function () {
for (let other of this._dockedGroup) {
if (other !== this) {
other.restore();
}
}
});
let lastBounds = this.getBounds();
this.on('move', function () {
const newBounds = this.getBounds();
// this._dockMoveTo(newBounds.x, newBounds.y, [lastBounds.x, lastBounds.y]);
lastBounds = newBounds;
});
this.on('resize', function () {
const newBounds = this.getBounds();
if (newBounds.width !== lastBounds.width || newBounds.height !== lastBounds.height) {
this.undock(); // TODO: Support changing size when docked.
}
// TODO: Handle resize positions of other docked windows
// This requires reworking how windows are docked/connected
// (they must be docked to edges of windows, not the windows themselves)
/* for (let index = 0; index < this._dockedGroup.length; index += 1) {
const other = this._dockedGroup[index];
if (other !== this) {
other.setPosition()
}
}*/
lastBounds = newBounds;
});
}
};
BrowserWindow.prototype.dock = function (otherID) {
this._ensureDockSystem();
// Resolve otherID, and fail if otherID doesn't exist.
const other = BrowserWindow.fromId(otherID);
if (other === undefined) { return; } // Failed to find other. TODO: Return error
// If other is already in the group, return:
if (this._dockedGroup.indexOf(other) >= 0) { return; }
// Make sure docked group exists:
other._ensureDockSystem();
// Loop through all windows in otherGroup and add them to this's group:
for (let otherWin of other._dockedGroup) {
this._dockedGroup.push(otherWin);
// Sharing the array between window objects makes it easier to manage:
otherWin._dockedGroup = this._dockedGroup;
}
// TODO: Check if otherGroup is touching
};
BrowserWindow.prototype.undock = function () {
this._ensureDockSystem();
// Check to see if window is already undocked:
if (this._dockedGroup.length === 1) { return; }
// Undock this:
this._dockedGroup.splice(this._dockedGroup.indexOf(this), 1);
this._dockedGroup = [this];
// TODO: Redock those still touching, EXCEPT 'this'.
};
BrowserWindow.prototype._dockFocus = function () {
this._ensureDockSystem();
for (let window of this._dockedGroup) {
if (window !== this) {
window.setAlwaysOnTop(true);
window.setAlwaysOnTop(false);
}
}
this.setAlwaysOnTop(true);
this.setAlwaysOnTop(false);
};
BrowserWindow.prototype._dragStart = function () {
// if (!this.emit('drag-start')) { return; } // Allow preventing drag
this._ensureDockSystem();
this.restore();
for (let window of this._dockedGroup) {
window._dragStartPos = window.getPosition();
}
};
BrowserWindow.prototype._getBounds = function () {
const bounds = this.getBounds();
return new BoundingBox(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height);
};
BrowserWindow.prototype._dragBy = function (deltaLeft, deltaTop) {
this._ensureDockSystem();
// Perform Snap:
const thisBounds = this._getBounds().moveTo(this._dragStartPos[0] + deltaLeft,
this._dragStartPos[1] + deltaTop);
let snapDelta = new Vector(NaN, NaN);
for (let other of BrowserWindow.getAllWindows()) {
if (other._dockedGroup !== this._dockedGroup) {
snapDelta.setMin(thisBounds.getSnapDelta(other._getBounds()));
}
}
deltaLeft += snapDelta.left || 0;
deltaTop += snapDelta.top || 0;
for (let other of this._dockedGroup) {
let pos = other._dragStartPos;
// If other doesn't have a drag position, start it:
if (pos === undefined) {
pos = other._dragStartPos = other.getPosition();
pos[0] -= deltaLeft;
pos[1] -= deltaTop;
}
other.setPosition(pos[0] + deltaLeft, pos[1] + deltaTop);
}
};
BrowserWindow.prototype._dragStop = function () {
this._ensureDockSystem();
// Dock to those it snapped to:
const thisBounds = this._getBounds();
for (let other of BrowserWindow.getAllWindows()) {
if (thisBounds.isTouching(other._getBounds())) {
this.dock(other.id);
}
}
for (let window of this._dockedGroup) {
delete window._dragStartPos;
}
};
BrowserWindow.prototype._dockMoveTo = function (left, top) {
this._ensureDockSystem();
const oldPos = this.getPosition();
const deltaLeft = left - oldPos[0];
const deltaTop = top - oldPos[1];
for (let other of this._dockedGroup) {
const pos = other.getPosition();
other.setPosition(pos[0] + deltaLeft, pos[1] + deltaTop);
}
};
BrowserWindow.prototype._dockMinimize = function (left, top) {
this._ensureDockSystem();
for (let window of this._dockedGroup) {
window.minimize();
}
};
BrowserWindow.prototype._dockHide = function (left, top) {
this._ensureDockSystem();
for (let window of this._dockedGroup) {
window.hide();
}
};
BrowserWindow.prototype._dockShow = function (left, top) {
this._ensureDockSystem();
for (let window of this._dockedGroup) {
window.show();
}
};