sprotty
Version:
A next-gen framework for graphical views
187 lines • 8.62 kB
JavaScript
;
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ATTR_BBOX_ELEMENT = exports.HiddenBoundsUpdater = exports.BoundsData = void 0;
const inversify_1 = require("inversify");
const actions_1 = require("sprotty-protocol/lib/actions");
const geometry_1 = require("sprotty-protocol/lib/utils/geometry");
const browser_1 = require("../../utils/browser");
const smodel_1 = require("../../base/model/smodel");
const types_1 = require("../../base/types");
const layout_1 = require("./layout");
const model_1 = require("./model");
class BoundsData {
}
exports.BoundsData = BoundsData;
/**
* Grabs the bounds from hidden SVG DOM elements, applies layouts and fires
* ComputedBoundsActions.
*
* The actual bounds of an element can usually not be determined from the SModel
* as they depend on the view implementation and CSS stylings. So the best way is
* to grab them from a live (but hidden) SVG using getBBox().
*
* If an element is Alignable, and the top-left corner of its bounding box is not
* the origin, we also issue a realign with the ComputedBoundsAction.
*/
let HiddenBoundsUpdater = class HiddenBoundsUpdater {
constructor() {
this.element2boundsData = new Map;
}
decorate(vnode, element) {
if ((0, model_1.isSizeable)(element) || (0, model_1.isLayoutContainer)(element)) {
this.element2boundsData.set(element, {
vnode: vnode,
bounds: element.bounds,
boundsChanged: false,
alignmentChanged: false
});
}
if (element instanceof smodel_1.SModelRootImpl) {
this.root = element;
}
return vnode;
}
postUpdate(cause) {
if (cause === undefined || cause.kind !== actions_1.RequestBoundsAction.KIND) {
return;
}
const request = cause;
this.getBoundsFromDOM();
this.layouter.layout(this.element2boundsData);
const resizes = [];
const alignments = [];
this.element2boundsData.forEach((boundsData, element) => {
if (boundsData.boundsChanged && boundsData.bounds !== undefined) {
const resize = {
elementId: element.id,
newSize: {
width: boundsData.bounds.width,
height: boundsData.bounds.height
}
};
// don't copy position if the element is layouted by the server
if (element instanceof smodel_1.SChildElementImpl && (0, model_1.isLayoutContainer)(element.parent)) {
resize.newPosition = {
x: boundsData.bounds.x,
y: boundsData.bounds.y,
};
}
resizes.push(resize);
}
if (boundsData.alignmentChanged && boundsData.alignment !== undefined) {
alignments.push({
elementId: element.id,
newAlignment: boundsData.alignment
});
}
});
const revision = (this.root !== undefined) ? this.root.revision : undefined;
this.actionDispatcher.dispatch(actions_1.ComputedBoundsAction.create(resizes, { revision, alignments, requestId: request.requestId }));
this.element2boundsData.clear();
}
getBoundsFromDOM() {
this.element2boundsData.forEach((boundsData, element) => {
if (boundsData.bounds && (0, model_1.isSizeable)(element)) {
const vnode = boundsData.vnode;
if (vnode && vnode.elm) {
const boundingBox = this.getBounds(vnode.elm, element);
if ((0, model_1.isAlignable)(element) && !((0, geometry_1.almostEquals)(boundingBox.x, 0) && (0, geometry_1.almostEquals)(boundingBox.y, 0))) {
boundsData.alignment = {
x: -boundingBox.x,
y: -boundingBox.y
};
boundsData.alignmentChanged = true;
}
const newBounds = {
x: element.bounds.x,
y: element.bounds.y,
width: boundingBox.width,
height: boundingBox.height
};
if (!((0, geometry_1.almostEquals)(newBounds.x, element.bounds.x)
&& (0, geometry_1.almostEquals)(newBounds.y, element.bounds.y)
&& (0, geometry_1.almostEquals)(newBounds.width, element.bounds.width)
&& (0, geometry_1.almostEquals)(newBounds.height, element.bounds.height))) {
boundsData.bounds = newBounds;
boundsData.boundsChanged = true;
}
}
}
});
}
/**
* Compute the bounds of the given DOM element. Override this method to customize how
* the bounding box of a rendered view is determined.
*
* In case your Sprotty model element contains children that are rendered outside of
* their parent, you can add the `ATTR_BBOX_ELEMENT` attribute to the SVG element
* that shall be used to compute the bounding box.
*/
getBounds(elm, element) {
if (!(0, browser_1.isSVGGraphicsElement)(elm)) {
this.logger.error(this, 'Not an SVG element:', elm);
return geometry_1.Bounds.EMPTY;
}
if (elm.tagName === 'g') {
for (const child of Array.from(elm.children)) {
if (child.getAttribute(exports.ATTR_BBOX_ELEMENT) !== null) {
return this.getBounds(child, element);
}
}
}
const bounds = elm.getBBox();
return {
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height
};
}
};
exports.HiddenBoundsUpdater = HiddenBoundsUpdater;
__decorate([
(0, inversify_1.inject)(types_1.TYPES.ILogger),
__metadata("design:type", Object)
], HiddenBoundsUpdater.prototype, "logger", void 0);
__decorate([
(0, inversify_1.inject)(types_1.TYPES.IActionDispatcher),
__metadata("design:type", Object)
], HiddenBoundsUpdater.prototype, "actionDispatcher", void 0);
__decorate([
(0, inversify_1.inject)(types_1.TYPES.Layouter),
__metadata("design:type", layout_1.Layouter)
], HiddenBoundsUpdater.prototype, "layouter", void 0);
exports.HiddenBoundsUpdater = HiddenBoundsUpdater = __decorate([
(0, inversify_1.injectable)()
], HiddenBoundsUpdater);
/**
* Attribute name identifying the SVG element that determines the bounding box of a rendered view.
* This can be used when a view creates a group of which only a part should contribute to the
* bounding box computed by `HiddenBoundsUpdater`.
*/
exports.ATTR_BBOX_ELEMENT = 'bboxElement';
//# sourceMappingURL=hidden-bounds-updater.js.map