mirror of
https://github.com/nasa/openmct.git
synced 2025-06-21 00:23:01 +00:00
[Toolbar] Implement a public API for adding toolbars (#1908)
* [API] Implement a toolbar registry and a plugin to allow providing a toolbar for a selected object. * Modify the mct-toolbar directive to get the toolbar structure from a provider based on selection. * Implements the layout toolbar in the layout bundle
This commit is contained in:
committed by
Andrew Henry
parent
de8f8d174d
commit
73e38f1955
@ -38,6 +38,24 @@ define(
|
||||
|
||||
var DEFAULT_DIMENSIONS = [2, 1];
|
||||
|
||||
// Convert from element x/y/width/height to an
|
||||
// appropriate ng-style argument, to position elements.
|
||||
function convertPosition(elementProxy) {
|
||||
if (elementProxy.getStyle) {
|
||||
return elementProxy.getStyle();
|
||||
}
|
||||
|
||||
var gridSize = elementProxy.getGridSize();
|
||||
|
||||
// Multiply position/dimensions by grid size
|
||||
return {
|
||||
left: (gridSize[0] * elementProxy.element.x) + 'px',
|
||||
top: (gridSize[1] * elementProxy.element.y) + 'px',
|
||||
width: (gridSize[0] * elementProxy.element.width) + 'px',
|
||||
height: (gridSize[1] * elementProxy.element.height) + 'px'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The FixedController is responsible for supporting the
|
||||
* Fixed Position view. It arranges frames according to saved
|
||||
@ -51,14 +69,14 @@ define(
|
||||
this.names = {}; // Cache names by ID
|
||||
this.values = {}; // Cache values by ID
|
||||
this.elementProxiesById = {};
|
||||
|
||||
this.telemetryObjects = [];
|
||||
this.subscriptions = [];
|
||||
this.telemetryObjects = {};
|
||||
this.subscriptions = {};
|
||||
this.openmct = openmct;
|
||||
this.$element = $element;
|
||||
this.$scope = $scope;
|
||||
|
||||
this.gridSize = $scope.domainObject && $scope.domainObject.getModel().layoutGrid;
|
||||
this.dialogService = dialogService;
|
||||
this.$q = $q;
|
||||
this.newDomainObject = $scope.domainObject.useCapability('adapter');
|
||||
this.fixedViewSelectable = false;
|
||||
|
||||
var self = this;
|
||||
@ -67,59 +85,13 @@ define(
|
||||
'fetchHistoricalData',
|
||||
'getTelemetry',
|
||||
'setDisplayedValue',
|
||||
'subscribeToObjects',
|
||||
'subscribeToObject',
|
||||
'unsubscribe',
|
||||
'updateView'
|
||||
].forEach(function (name) {
|
||||
self[name] = self[name].bind(self);
|
||||
});
|
||||
|
||||
// Convert from element x/y/width/height to an
|
||||
// appropriate ng-style argument, to position elements.
|
||||
function convertPosition(elementProxy) {
|
||||
var gridSize = elementProxy.getGridSize();
|
||||
// Multiply position/dimensions by grid size
|
||||
return {
|
||||
left: (gridSize[0] * elementProxy.x()) + 'px',
|
||||
top: (gridSize[1] * elementProxy.y()) + 'px',
|
||||
width: (gridSize[0] * elementProxy.width()) + 'px',
|
||||
height: (gridSize[1] * elementProxy.height()) + 'px'
|
||||
};
|
||||
}
|
||||
|
||||
// Update the style for a selected element
|
||||
function updateSelectionStyle() {
|
||||
if (self.selectedElementProxy) {
|
||||
self.selectedElementProxy.style = convertPosition(self.selectedElementProxy);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a specific drag handle
|
||||
function generateDragHandle(elementHandle) {
|
||||
return new FixedDragHandle(
|
||||
elementHandle,
|
||||
self.gridSize,
|
||||
updateSelectionStyle,
|
||||
$scope.commit
|
||||
);
|
||||
}
|
||||
|
||||
// Generate drag handles for an element
|
||||
function generateDragHandles(element) {
|
||||
return element.handles().map(generateDragHandle);
|
||||
}
|
||||
|
||||
// Update element positions when grid size changes
|
||||
function updateElementPositions(layoutGrid) {
|
||||
// Update grid size from model
|
||||
self.gridSize = layoutGrid;
|
||||
|
||||
self.elementProxies.forEach(function (elementProxy) {
|
||||
elementProxy.setGridSize(self.gridSize);
|
||||
elementProxy.style = convertPosition(elementProxy);
|
||||
});
|
||||
}
|
||||
|
||||
// Decorate an element for display
|
||||
function makeProxyElement(element, index, elements) {
|
||||
var ElementProxy = ElementProxies[element.type],
|
||||
@ -137,14 +109,14 @@ define(
|
||||
|
||||
// Decorate elements in the current configuration
|
||||
function refreshElements() {
|
||||
var elements = (($scope.configuration || {}).elements || []);
|
||||
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
|
||||
|
||||
// Create the new proxies...
|
||||
self.elementProxies = elements.map(makeProxyElement);
|
||||
|
||||
// If selection is not in array, select parent.
|
||||
// Otherwise, set the element to select after refresh.
|
||||
if (self.selectedElementProxy) {
|
||||
// If selection is not in array, select parent.
|
||||
// Otherwise, set the element to select after refresh.
|
||||
var index = elements.indexOf(self.selectedElementProxy.element);
|
||||
if (index === -1) {
|
||||
self.$element[0].click();
|
||||
@ -168,79 +140,53 @@ define(
|
||||
});
|
||||
}
|
||||
|
||||
function removeObjects(ids) {
|
||||
var configuration = self.$scope.configuration;
|
||||
|
||||
if (configuration &&
|
||||
configuration.elements) {
|
||||
configuration.elements = configuration.elements.filter(function (proxy) {
|
||||
return ids.indexOf(proxy.id) === -1;
|
||||
});
|
||||
}
|
||||
self.getTelemetry($scope.domainObject);
|
||||
refreshElements();
|
||||
// Mark change as persistable
|
||||
if (self.$scope.commit) {
|
||||
self.$scope.commit("Objects removed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle changes in the object's composition
|
||||
function updateComposition(composition, previousComposition) {
|
||||
var removedIds = [];
|
||||
// Resubscribe - objects in view have changed
|
||||
if (composition !== previousComposition) {
|
||||
//remove any elements no longer in the composition
|
||||
removedIds = _.difference(previousComposition, composition);
|
||||
if (removedIds.length > 0) {
|
||||
removeObjects(removedIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger a new query for telemetry data
|
||||
function updateDisplayBounds(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
//Reset values
|
||||
self.values = {};
|
||||
refreshElements();
|
||||
|
||||
//Fetch new data
|
||||
self.fetchHistoricalData(self.telemetryObjects);
|
||||
Object.values(self.telemetryObjects).forEach(function (object) {
|
||||
self.fetchHistoricalData(object);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add an element to this view
|
||||
function addElement(element) {
|
||||
// Ensure that configuration field is populated
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
// Make sure there is a "elements" field in the
|
||||
// view configuration.
|
||||
$scope.configuration.elements =
|
||||
$scope.configuration.elements || [];
|
||||
// Store the position of this element.
|
||||
$scope.configuration.elements.push(element);
|
||||
var index;
|
||||
var elements = (((self.newDomainObject.configuration || {})['fixed-display'] || {}).elements || []);
|
||||
elements.push(element);
|
||||
|
||||
self.elementToSelectAfterRefresh = element;
|
||||
|
||||
// Refresh displayed elements
|
||||
refreshElements();
|
||||
|
||||
// Mark change as persistable
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Dropped an element.");
|
||||
if (self.selectedElementProxy) {
|
||||
index = elements.indexOf(self.selectedElementProxy.element);
|
||||
}
|
||||
|
||||
self.mutate("configuration['fixed-display'].elements", elements);
|
||||
elements = (self.newDomainObject.configuration)['fixed-display'].elements || [];
|
||||
self.elementToSelectAfterRefresh = elements[elements.length - 1];
|
||||
|
||||
if (self.selectedElementProxy) {
|
||||
// Update the selected element with the new
|
||||
// value since newDomainOject is mutated.
|
||||
self.selectedElementProxy.element = elements[index];
|
||||
}
|
||||
refreshElements();
|
||||
}
|
||||
|
||||
// Position a panel after a drop event
|
||||
function handleDrop(e, id, position) {
|
||||
// Don't handle this event if it has already been handled
|
||||
// color is set to "" to let the CSS theme determine the default color
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Store the position of this element.
|
||||
// color is set to "" to let the CSS theme determine the default color
|
||||
addElement({
|
||||
type: "fixed.telemetry",
|
||||
x: Math.floor(position.x / self.gridSize[0]),
|
||||
@ -254,71 +200,229 @@ define(
|
||||
useGrid: true
|
||||
});
|
||||
|
||||
//Re-initialize objects, and subscribe to new object
|
||||
self.getTelemetry($scope.domainObject);
|
||||
}
|
||||
|
||||
// Sets the selectable object in response to the selection change event.
|
||||
function setSelection(selectable) {
|
||||
var selection = selectable[0];
|
||||
|
||||
if (!selection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection.context.elementProxy) {
|
||||
self.selectedElementProxy = selection.context.elementProxy;
|
||||
self.mvHandle = self.generateDragHandle(self.selectedElementProxy);
|
||||
self.resizeHandles = self.generateDragHandles(self.selectedElementProxy);
|
||||
} else {
|
||||
// Make fixed view selectable if it's not already.
|
||||
if (!self.fixedViewSelectable && selectable.length === 1) {
|
||||
self.fixedViewSelectable = true;
|
||||
selection.context.viewProxy = new FixedProxy(addElement, $q, dialogService);
|
||||
self.openmct.selection.select(selection);
|
||||
}
|
||||
|
||||
self.resizeHandles = [];
|
||||
self.mvHandle = undefined;
|
||||
self.selectedElementProxy = undefined;
|
||||
}
|
||||
// Subscribe to the new object to get telemetry
|
||||
self.openmct.objects.get(id).then(function (object) {
|
||||
self.getTelemetry(object);
|
||||
});
|
||||
}
|
||||
|
||||
this.elementProxies = [];
|
||||
this.generateDragHandle = generateDragHandle;
|
||||
this.generateDragHandles = generateDragHandles;
|
||||
this.updateSelectionStyle = updateSelectionStyle;
|
||||
this.addElement = addElement;
|
||||
this.refreshElements = refreshElements;
|
||||
this.fixedProxy = new FixedProxy(this.addElement, this.$q, this.dialogService);
|
||||
|
||||
// Detect changes to grid size
|
||||
$scope.$watch("model.layoutGrid", updateElementPositions);
|
||||
this.composition = this.openmct.composition.get(this.newDomainObject);
|
||||
this.composition.on('add', this.onCompositionAdd, this);
|
||||
this.composition.on('remove', this.onCompositionRemove, this);
|
||||
this.composition.load();
|
||||
|
||||
// Position panes where they are dropped
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
|
||||
// Position panes when the model field changes
|
||||
$scope.$watch("model.composition", updateComposition);
|
||||
|
||||
// Refresh list of elements whenever model changes
|
||||
$scope.$watch("model.modified", refreshElements);
|
||||
|
||||
// Subscribe to telemetry when an object is available
|
||||
$scope.$watch("domainObject", this.getTelemetry);
|
||||
|
||||
// Free up subscription on destroy
|
||||
$scope.$on("$destroy", function () {
|
||||
self.unsubscribe();
|
||||
self.openmct.time.off("bounds", updateDisplayBounds);
|
||||
self.openmct.selection.off("change", setSelection);
|
||||
});
|
||||
$scope.$on("$destroy", this.destroy.bind(this));
|
||||
|
||||
// Respond to external bounds changes
|
||||
this.openmct.time.on("bounds", updateDisplayBounds);
|
||||
this.openmct.selection.on('change', setSelection);
|
||||
this.$element.on('click', this.bypassSelection.bind(this));
|
||||
|
||||
setSelection(this.openmct.selection.get());
|
||||
this.openmct.selection.on('change', this.setSelection.bind(this));
|
||||
this.$element.on('click', this.bypassSelection.bind(this));
|
||||
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
|
||||
this.newDomainObject = JSON.parse(JSON.stringify(obj));
|
||||
this.updateElementPositions(this.newDomainObject.layoutGrid);
|
||||
}.bind(this));
|
||||
|
||||
this.updateElementPositions(this.newDomainObject.layoutGrid);
|
||||
refreshElements();
|
||||
}
|
||||
|
||||
FixedController.prototype.updateElementPositions = function (layoutGrid) {
|
||||
this.gridSize = layoutGrid;
|
||||
|
||||
this.elementProxies.forEach(function (elementProxy) {
|
||||
elementProxy.setGridSize(this.gridSize);
|
||||
elementProxy.style = convertPosition(elementProxy);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
FixedController.prototype.onCompositionAdd = function (object) {
|
||||
this.getTelemetry(object);
|
||||
};
|
||||
|
||||
FixedController.prototype.onCompositionRemove = function (identifier) {
|
||||
// Defer mutation of newDomainObject to prevent mutating an
|
||||
// outdated version since this is triggered by a composition change.
|
||||
setTimeout(function () {
|
||||
var id = objectUtils.makeKeyString(identifier);
|
||||
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
|
||||
var newElements = elements.filter(function (proxy) {
|
||||
return proxy.id !== id;
|
||||
});
|
||||
this.mutate("configuration['fixed-display'].elements", newElements);
|
||||
|
||||
if (this.subscriptions[id]) {
|
||||
this.subscriptions[id]();
|
||||
delete this.subscriptions[id];
|
||||
}
|
||||
|
||||
delete this.telemetryObjects[id];
|
||||
this.refreshElements();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an element from the view.
|
||||
*
|
||||
* @param {Object} elementProxy the element proxy to remove.
|
||||
*/
|
||||
FixedController.prototype.remove = function (elementProxy) {
|
||||
var element = elementProxy.element;
|
||||
var elements = this.newDomainObject.configuration['fixed-display'].elements || [];
|
||||
elements.splice(elements.indexOf(element), 1);
|
||||
|
||||
if (element.type === 'fixed.telemetry') {
|
||||
this.newDomainObject.composition = this.newDomainObject.composition.filter(function (identifier) {
|
||||
return objectUtils.makeKeyString(identifier) !== element.id;
|
||||
});
|
||||
}
|
||||
|
||||
this.mutate("configuration['fixed-display'].elements", elements);
|
||||
this.refreshElements();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a new element to the view.
|
||||
*
|
||||
* @param {string} type the type of element to add. Supported types are:
|
||||
* `fixed.image`
|
||||
* `fixed.box`
|
||||
* `fixed.text`
|
||||
* `fixed.line`
|
||||
*/
|
||||
FixedController.prototype.add = function (type) {
|
||||
this.fixedProxy.add(type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the display order of the element proxy.
|
||||
*/
|
||||
FixedController.prototype.order = function (elementProxy, position) {
|
||||
var elements = elementProxy.order(position);
|
||||
|
||||
// Find the selected element index in the updated array.
|
||||
var selectedElemenetIndex = elements.indexOf(this.selectedElementProxy.element);
|
||||
|
||||
this.mutate("configuration['fixed-display'].elements", elements);
|
||||
elements = (this.newDomainObject.configuration)['fixed-display'].elements || [];
|
||||
|
||||
// Update the selected element with the new
|
||||
// value since newDomainOject is mutated.
|
||||
this.selectedElementProxy.element = elements[selectedElemenetIndex];
|
||||
this.refreshElements();
|
||||
};
|
||||
|
||||
FixedController.prototype.generateDragHandle = function (elementProxy, elementHandle) {
|
||||
var index = this.elementProxies.indexOf(elementProxy);
|
||||
|
||||
if (elementHandle) {
|
||||
elementHandle.element = elementProxy.element;
|
||||
elementProxy = elementHandle;
|
||||
}
|
||||
|
||||
return new FixedDragHandle(
|
||||
elementProxy,
|
||||
"configuration['fixed-display'].elements[" + index + "]",
|
||||
this
|
||||
);
|
||||
};
|
||||
|
||||
FixedController.prototype.generateDragHandles = function (elementProxy) {
|
||||
return elementProxy.handles().map(function (handle) {
|
||||
return this.generateDragHandle(elementProxy, handle);
|
||||
}, this);
|
||||
};
|
||||
|
||||
FixedController.prototype.updateSelectionStyle = function () {
|
||||
this.selectedElementProxy.style = convertPosition(this.selectedElementProxy);
|
||||
};
|
||||
|
||||
FixedController.prototype.setSelection = function (selectable) {
|
||||
var selection = selectable[0];
|
||||
|
||||
if (this.selectionListeners) {
|
||||
this.selectionListeners.forEach(function (l) {
|
||||
l();
|
||||
});
|
||||
}
|
||||
|
||||
this.selectionListeners = [];
|
||||
|
||||
if (!selection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection.context.elementProxy) {
|
||||
this.selectedElementProxy = selection.context.elementProxy;
|
||||
this.attachSelectionListeners();
|
||||
this.mvHandle = this.generateDragHandle(this.selectedElementProxy);
|
||||
this.resizeHandles = this.generateDragHandles(this.selectedElementProxy);
|
||||
} else {
|
||||
// Make fixed view selectable if it's not already.
|
||||
if (!this.fixedViewSelectable && selectable.length === 1) {
|
||||
this.fixedViewSelectable = true;
|
||||
selection.context.fixedController = this;
|
||||
this.openmct.selection.select(selection);
|
||||
}
|
||||
|
||||
this.resizeHandles = [];
|
||||
this.mvHandle = undefined;
|
||||
this.selectedElementProxy = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
FixedController.prototype.attachSelectionListeners = function () {
|
||||
var index = this.elementProxies.indexOf(this.selectedElementProxy);
|
||||
var path = "configuration['fixed-display'].elements[" + index + "]";
|
||||
|
||||
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + ".useGrid", function (newValue) {
|
||||
if (this.selectedElementProxy.useGrid() !== newValue) {
|
||||
this.selectedElementProxy.useGrid(newValue);
|
||||
this.updateSelectionStyle();
|
||||
this.openmct.objects.mutate(this.newDomainObject, path, this.selectedElementProxy.element);
|
||||
}
|
||||
}.bind(this)));
|
||||
[
|
||||
"width",
|
||||
"height",
|
||||
"stroke",
|
||||
"fill",
|
||||
"x",
|
||||
"y",
|
||||
"x1",
|
||||
"y1",
|
||||
"x2",
|
||||
"y2",
|
||||
"color",
|
||||
"size",
|
||||
"text",
|
||||
"titled"
|
||||
].forEach(function (property) {
|
||||
this.selectionListeners.push(this.openmct.objects.observe(this.newDomainObject, path + "." + property, function (newValue) {
|
||||
this.selectedElementProxy.element[property] = newValue;
|
||||
this.updateSelectionStyle();
|
||||
}.bind(this)));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
FixedController.prototype.destroy = function () {
|
||||
this.unsubscribe();
|
||||
this.unlisten();
|
||||
this.openmct.time.off("bounds", this.updateDisplayBounds);
|
||||
this.openmct.selection.off("change", this.setSelection);
|
||||
this.composition.off('add', this.onCompositionAdd, this);
|
||||
this.composition.off('remove', this.onCompositionRemove, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* A rate-limited digest function. Caps digests at 60Hz
|
||||
* @private
|
||||
@ -340,31 +444,30 @@ define(
|
||||
* @private
|
||||
*/
|
||||
FixedController.prototype.unsubscribe = function () {
|
||||
this.subscriptions.forEach(function (unsubscribeFunc) {
|
||||
Object.values(this.subscriptions).forEach(function (unsubscribeFunc) {
|
||||
unsubscribeFunc();
|
||||
});
|
||||
this.subscriptions = [];
|
||||
this.telemetryObjects = [];
|
||||
this.subscriptions = {};
|
||||
this.telemetryObjects = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribe to all given domain objects
|
||||
* Subscribe to the given domain object
|
||||
* @private
|
||||
* @param {object[]} objects Domain objects to subscribe to
|
||||
* @returns {object[]} The provided objects, for chaining.
|
||||
* @param {object} object Domain object to subscribe to
|
||||
* @returns {object} The provided object, for chaining.
|
||||
*/
|
||||
FixedController.prototype.subscribeToObjects = function (objects) {
|
||||
FixedController.prototype.subscribeToObject = function (object) {
|
||||
var self = this;
|
||||
var timeAPI = this.openmct.time;
|
||||
var id = objectUtils.makeKeyString(object.identifier);
|
||||
this.subscriptions[id] = self.openmct.telemetry.subscribe(object, function (datum) {
|
||||
if (timeAPI.clock() !== undefined) {
|
||||
self.updateView(object, datum);
|
||||
}
|
||||
}, {});
|
||||
|
||||
this.subscriptions = objects.map(function (object) {
|
||||
return self.openmct.telemetry.subscribe(object, function (datum) {
|
||||
if (timeAPI.clock() !== undefined) {
|
||||
self.updateView(object, datum);
|
||||
}
|
||||
}, {});
|
||||
});
|
||||
return objects;
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -416,23 +519,22 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* Request the last historical data point for the given domain objects
|
||||
* @param {object[]} objects
|
||||
* @returns {object[]} the provided objects for chaining.
|
||||
* Request the last historical data point for the given domain object
|
||||
* @param {object} object
|
||||
* @returns {object} the provided object for chaining.
|
||||
*/
|
||||
FixedController.prototype.fetchHistoricalData = function (objects) {
|
||||
FixedController.prototype.fetchHistoricalData = function (object) {
|
||||
var bounds = this.openmct.time.bounds();
|
||||
var self = this;
|
||||
|
||||
objects.forEach(function (object) {
|
||||
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
|
||||
.then(function (data) {
|
||||
if (data.length > 0) {
|
||||
self.updateView(object, data[data.length - 1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
return objects;
|
||||
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
|
||||
.then(function (data) {
|
||||
if (data.length > 0) {
|
||||
self.updateView(object, data[data.length - 1]);
|
||||
}
|
||||
});
|
||||
|
||||
return object;
|
||||
};
|
||||
|
||||
|
||||
@ -457,33 +559,25 @@ define(
|
||||
};
|
||||
|
||||
FixedController.prototype.getTelemetry = function (domainObject) {
|
||||
var newObject = domainObject.useCapability('adapter');
|
||||
var self = this;
|
||||
var id = objectUtils.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (this.subscriptions.length > 0) {
|
||||
this.unsubscribe();
|
||||
if (this.subscriptions[id]) {
|
||||
this.subscriptions[id]();
|
||||
delete this.subscriptions[id];
|
||||
}
|
||||
delete this.telemetryObjects[id];
|
||||
|
||||
if (!this.openmct.telemetry.isTelemetryObject(domainObject)) {
|
||||
return;
|
||||
}
|
||||
|
||||
function filterForTelemetryObjects(objects) {
|
||||
return objects.filter(function (object) {
|
||||
return self.openmct.telemetry.isTelemetryObject(object);
|
||||
});
|
||||
}
|
||||
// Initialize display
|
||||
this.telemetryObjects[id] = domainObject;
|
||||
this.setDisplayedValue(domainObject, "");
|
||||
|
||||
function initializeDisplay(objects) {
|
||||
self.telemetryObjects = objects;
|
||||
objects.forEach(function (object) {
|
||||
// Initialize values
|
||||
self.setDisplayedValue(object, "");
|
||||
});
|
||||
return objects;
|
||||
}
|
||||
|
||||
return this.openmct.composition.get(newObject).load()
|
||||
.then(filterForTelemetryObjects)
|
||||
.then(initializeDisplay)
|
||||
return Promise.resolve(domainObject)
|
||||
.then(this.fetchHistoricalData)
|
||||
.then(this.subscribeToObjects);
|
||||
.then(this.subscribeToObject);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -580,12 +674,12 @@ define(
|
||||
* Gets the selection context.
|
||||
*
|
||||
* @param elementProxy the element proxy
|
||||
* @returns {object} the context object which includes elementProxy and toolbar
|
||||
* @returns {object} the context object which includes elementProxy
|
||||
*/
|
||||
FixedController.prototype.getContext = function (elementProxy) {
|
||||
return {
|
||||
elementProxy: elementProxy,
|
||||
toolbar: elementProxy
|
||||
fixedController: this
|
||||
};
|
||||
};
|
||||
|
||||
@ -608,6 +702,10 @@ define(
|
||||
}
|
||||
};
|
||||
|
||||
FixedController.prototype.mutate = function (path, value) {
|
||||
this.openmct.objects.mutate(this.newDomainObject, path, value);
|
||||
};
|
||||
|
||||
return FixedController;
|
||||
}
|
||||
);
|
||||
|
@ -24,30 +24,34 @@ define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
|
||||
// Drag handle dimensions
|
||||
var DRAG_HANDLE_SIZE = [6, 6];
|
||||
|
||||
/**
|
||||
* Template-displayable drag handle for an element in fixed
|
||||
* position mode.
|
||||
*
|
||||
* @param elementHandle the element handle
|
||||
* @param configPath the configuration path of an element
|
||||
* @param {Object} fixedControl the fixed controller
|
||||
* @memberof platform/features/layout
|
||||
* @constructor
|
||||
*/
|
||||
function FixedDragHandle(elementHandle, gridSize, update, commit) {
|
||||
function FixedDragHandle(elementHandle, configPath, fixedControl) {
|
||||
this.elementHandle = elementHandle;
|
||||
this.gridSize = gridSize;
|
||||
this.update = update;
|
||||
this.commit = commit;
|
||||
this.configPath = configPath;
|
||||
this.fixedControl = fixedControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a CSS style to position this drag handle.
|
||||
*
|
||||
* @returns CSS style object (for `ng-style`)
|
||||
* @memberof platform/features/layout.FixedDragHandle#
|
||||
*/
|
||||
FixedDragHandle.prototype.style = function () {
|
||||
var gridSize = this.elementHandle.getGridSize();
|
||||
|
||||
// Adjust from grid to pixel coordinates
|
||||
var x = this.elementHandle.x() * gridSize[0],
|
||||
y = this.elementHandle.y() * gridSize[1];
|
||||
@ -75,23 +79,20 @@ define(
|
||||
|
||||
/**
|
||||
* Continue a drag gesture; update x/y positions.
|
||||
* @param {number[]} delta x/y pixel difference since drag
|
||||
* started
|
||||
*
|
||||
* @param {number[]} delta x/y pixel difference since drag started
|
||||
*/
|
||||
FixedDragHandle.prototype.continueDrag = function (delta) {
|
||||
var gridSize = this.elementHandle.getGridSize();
|
||||
|
||||
if (this.dragging) {
|
||||
// Update x/y positions (snapping to grid)
|
||||
this.elementHandle.x(
|
||||
this.dragging.x + Math.round(delta[0] / gridSize[0])
|
||||
);
|
||||
this.elementHandle.y(
|
||||
this.dragging.y + Math.round(delta[1] / gridSize[1])
|
||||
);
|
||||
// Invoke update callback
|
||||
if (this.update) {
|
||||
this.update();
|
||||
}
|
||||
var newX = this.dragging.x + Math.round(delta[0] / gridSize[0]);
|
||||
var newY = this.dragging.y + Math.round(delta[1] / gridSize[1]);
|
||||
|
||||
this.elementHandle.x(Math.max(0, newX));
|
||||
this.elementHandle.y(Math.max(0, newY));
|
||||
this.fixedControl.updateSelectionStyle();
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,12 +101,8 @@ define(
|
||||
* concludes to trigger commit of changes.
|
||||
*/
|
||||
FixedDragHandle.prototype.endDrag = function () {
|
||||
// Clear cached state
|
||||
this.dragging = undefined;
|
||||
// Mark change as complete
|
||||
if (this.commit) {
|
||||
this.commit("Dragged handle.");
|
||||
}
|
||||
this.fixedControl.mutate(this.configPath, this.elementHandle.element);
|
||||
};
|
||||
|
||||
return FixedDragHandle;
|
||||
|
@ -78,29 +78,30 @@ define(
|
||||
}
|
||||
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
$scope.configuration.panels =
|
||||
$scope.configuration.panels || {};
|
||||
$scope.configuration.panels = $scope.configuration.panels || {};
|
||||
|
||||
$scope.configuration.panels[id] = {
|
||||
position: [
|
||||
Math.floor(position.x / self.gridSize[0]),
|
||||
Math.floor(position.y / self.gridSize[1])
|
||||
],
|
||||
dimensions: self.defaultDimensions()
|
||||
};
|
||||
self.openmct.objects.get(id).then(function (object) {
|
||||
$scope.configuration.panels[id] = {
|
||||
position: [
|
||||
Math.floor(position.x / self.gridSize[0]),
|
||||
Math.floor(position.y / self.gridSize[1])
|
||||
],
|
||||
dimensions: self.defaultDimensions(),
|
||||
hasFrame: self.getDefaultFrame(object.type)
|
||||
};
|
||||
|
||||
// Store the id so that the newly-dropped object
|
||||
// gets selected during refresh composition
|
||||
self.droppedIdToSelectAfterRefresh = id;
|
||||
// Store the id so that the newly-dropped object
|
||||
// gets selected during refresh composition
|
||||
self.droppedIdToSelectAfterRefresh = id;
|
||||
|
||||
self.commit();
|
||||
|
||||
// Populate template-facing position for this id
|
||||
self.rawPositions[id] = $scope.configuration.panels[id];
|
||||
self.populatePosition(id);
|
||||
refreshComposition();
|
||||
});
|
||||
|
||||
// Mark change as persistable
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Dropped a frame.");
|
||||
}
|
||||
// Populate template-facing position for this id
|
||||
self.rawPositions[id] =
|
||||
$scope.configuration.panels[id];
|
||||
self.populatePosition(id);
|
||||
// Layout may contain embedded views which will
|
||||
// listen for drops, so call preventDefault() so
|
||||
// that they can recognize that this event is handled.
|
||||
@ -157,10 +158,7 @@ define(
|
||||
$scope.configuration.panels[self.activeDragId].dimensions =
|
||||
self.rawPositions[self.activeDragId].dimensions;
|
||||
|
||||
// Mark this object as dirty to encourage persistence
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Moved frame.");
|
||||
}
|
||||
self.commit();
|
||||
};
|
||||
|
||||
// Sets the selectable object in response to the selection change event.
|
||||
@ -194,9 +192,22 @@ define(
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
openmct.selection.off("change", setSelection);
|
||||
self.unlisten();
|
||||
});
|
||||
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
|
||||
self.unlisten = self.$scope.domainObject.getCapability('mutation').listen(function (model) {
|
||||
$scope.configuration = model.configuration.layout;
|
||||
$scope.model = model;
|
||||
var panels = $scope.configuration.panels;
|
||||
|
||||
Object.keys(panels).forEach(function (key) {
|
||||
if (self.frames && self.frames.hasOwnProperty(key)) {
|
||||
self.frames[key] = panels[key].hasFrame;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to copy raw positions from configuration,
|
||||
@ -220,7 +231,6 @@ define(
|
||||
*/
|
||||
LayoutController.prototype.setFrames = function (ids) {
|
||||
var panels = shallowCopy(this.$scope.configuration.panels || {}, ids);
|
||||
|
||||
this.frames = {};
|
||||
|
||||
this.$scope.composition.forEach(function (object) {
|
||||
@ -230,11 +240,22 @@ define(
|
||||
if (panels[id].hasOwnProperty('hasFrame')) {
|
||||
this.frames[id] = panels[id].hasFrame;
|
||||
} else {
|
||||
this.frames[id] = DEFAULT_HIDDEN_FRAME_TYPES.indexOf(object.getModel().type) === -1;
|
||||
this.frames[id] = this.getDefaultFrame(object.getModel().type);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default value for frame.
|
||||
*
|
||||
* @param type the domain object type
|
||||
* @return {boolean} true if the object should have
|
||||
* frame by default, false, otherwise
|
||||
*/
|
||||
LayoutController.prototype.getDefaultFrame = function (type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
};
|
||||
|
||||
// Convert from { positions: ..., dimensions: ... } to an
|
||||
// appropriate ng-style argument, to position frames.
|
||||
LayoutController.prototype.convertPosition = function (raw) {
|
||||
@ -389,40 +410,6 @@ define(
|
||||
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to show/hide the object frame.
|
||||
*
|
||||
* @param {string} id the object id
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.toggleFrame = function (id, domainObject) {
|
||||
var configuration = this.$scope.configuration;
|
||||
|
||||
if (!configuration.panels[id]) {
|
||||
configuration.panels[id] = {};
|
||||
}
|
||||
|
||||
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
|
||||
|
||||
var selection = this.openmct.selection.get();
|
||||
selection[0].context.toolbar = this.getToolbar(id, domainObject);
|
||||
this.openmct.selection.select(selection); // reselect so toolbar updates
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the toolbar object for the given domain object.
|
||||
*
|
||||
* @param id the domain object id
|
||||
* @param domainObject the domain object
|
||||
* @returns {object}
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.getToolbar = function (id, domainObject) {
|
||||
var toolbarObj = {};
|
||||
toolbarObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id, domainObject);
|
||||
return toolbarObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bypasses selection if drag is in progress.
|
||||
*
|
||||
@ -497,17 +484,25 @@ define(
|
||||
* Gets the selection context.
|
||||
*
|
||||
* @param domainObject the domain object
|
||||
* @returns {object} the context object which includes
|
||||
* item, oldItem and toolbar
|
||||
* @returns {object} the context object which includes item and oldItem
|
||||
*/
|
||||
LayoutController.prototype.getContext = function (domainObject, toolbar) {
|
||||
LayoutController.prototype.getContext = function (domainObject) {
|
||||
return {
|
||||
item: domainObject.useCapability('adapter'),
|
||||
oldItem: domainObject,
|
||||
toolbar: toolbar ? this.getToolbar(domainObject.getId(), domainObject) : undefined
|
||||
oldItem: domainObject
|
||||
};
|
||||
};
|
||||
|
||||
LayoutController.prototype.commit = function () {
|
||||
var model = this.$scope.model;
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.layout = this.$scope.configuration;
|
||||
|
||||
this.$scope.domainObject.useCapability('mutation', function () {
|
||||
return model;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects a newly-dropped object.
|
||||
*
|
||||
|
@ -53,12 +53,6 @@ define(
|
||||
*/
|
||||
proxy.fill = new AccessorMutator(element, 'fill');
|
||||
|
||||
//Expose x,y, width and height for editing
|
||||
proxy.editWidth = new AccessorMutator(element, 'width');
|
||||
proxy.editHeight = new AccessorMutator(element, 'height');
|
||||
proxy.editX = new AccessorMutator(element, 'x');
|
||||
proxy.editY = new AccessorMutator(element, 'y');
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
@ -71,13 +71,6 @@ define(
|
||||
*/
|
||||
this.gridSize = gridSize || [1,1]; //Ensure a reasonable default
|
||||
|
||||
this.resizeHandles = [new ResizeHandle(
|
||||
this.element,
|
||||
this.getMinWidth(),
|
||||
this.getMinHeight(),
|
||||
this.getGridSize()
|
||||
)];
|
||||
|
||||
/**
|
||||
* Get and/or set the x position of this element.
|
||||
* Units are in fixed position grid space.
|
||||
@ -123,15 +116,16 @@ define(
|
||||
this.height = new AccessorMutator(element, 'height');
|
||||
|
||||
this.useGrid = new UnitAccessorMutator(this);
|
||||
|
||||
this.index = index;
|
||||
this.elements = elements;
|
||||
this.resizeHandles = [new ResizeHandle(this, this.element)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the display order of this element.
|
||||
* @param {string} o where to move this element;
|
||||
* one of "top", "up", "down", or "bottom"
|
||||
* @return {Array} the full array of elements
|
||||
*/
|
||||
ElementProxy.prototype.order = function (o) {
|
||||
var index = this.index,
|
||||
@ -152,16 +146,8 @@ define(
|
||||
// anyway, but be consistent)
|
||||
this.index = desired;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove this element from the fixed position view.
|
||||
*/
|
||||
ElementProxy.prototype.remove = function () {
|
||||
var index = this.index;
|
||||
if (this.elements[index] === this.element) {
|
||||
this.elements.splice(index, 1);
|
||||
}
|
||||
return elements;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -208,7 +194,6 @@ define(
|
||||
*/
|
||||
ElementProxy.prototype.getMinWidth = function () {
|
||||
return Math.ceil(MIN_WIDTH / this.getGridSize()[0]);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -50,12 +50,6 @@ define(
|
||||
*/
|
||||
proxy.url = new AccessorMutator(element, 'url');
|
||||
|
||||
//Expose x,y, width and height properties for editing
|
||||
proxy.editWidth = new AccessorMutator(element, 'width');
|
||||
proxy.editHeight = new AccessorMutator(element, 'height');
|
||||
proxy.editX = new AccessorMutator(element, 'x');
|
||||
proxy.editY = new AccessorMutator(element, 'y');
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
@ -32,19 +32,18 @@ define(
|
||||
* @constructor
|
||||
* @param element the line element
|
||||
* @param {string} xProperty field which stores x position
|
||||
* @param {string} yProperty field which stores x position
|
||||
* @param {string} yProperty field which stores y position
|
||||
* @param {string} xOther field which stores x of other end
|
||||
* @param {string} yOther field which stores y of other end
|
||||
* @param {number[]} gridSize the current layout grid size in [x,y] from
|
||||
* @implements {platform/features/layout.ElementHandle}
|
||||
*/
|
||||
function LineHandle(element, xProperty, yProperty, xOther, yOther, gridSize) {
|
||||
function LineHandle(element, elementProxy, xProperty, yProperty, xOther, yOther) {
|
||||
this.elementProxy = elementProxy;
|
||||
this.element = element;
|
||||
this.xProperty = xProperty;
|
||||
this.yProperty = yProperty;
|
||||
this.xOther = xOther;
|
||||
this.yOther = yOther;
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
LineHandle.prototype.x = function (value) {
|
||||
@ -86,7 +85,7 @@ define(
|
||||
};
|
||||
|
||||
LineHandle.prototype.getGridSize = function () {
|
||||
return this.gridSize;
|
||||
return this.elementProxy.getGridSize();
|
||||
};
|
||||
|
||||
return LineHandle;
|
||||
|
@ -39,10 +39,24 @@ define(
|
||||
function LineProxy(element, index, elements, gridSize) {
|
||||
var proxy = new ElementProxy(element, index, elements, gridSize),
|
||||
handles = [
|
||||
new LineHandle(element, 'x', 'y', 'x2', 'y2', proxy.getGridSize()),
|
||||
new LineHandle(element, 'x2', 'y2', 'x', 'y', proxy.getGridSize())
|
||||
new LineHandle(element, proxy, 'x', 'y', 'x2', 'y2'),
|
||||
new LineHandle(element, proxy, 'x2', 'y2', 'x', 'y')
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets style specific to line proxy.
|
||||
*/
|
||||
proxy.getStyle = function () {
|
||||
var layoutGridSize = proxy.getGridSize();
|
||||
|
||||
return {
|
||||
left: (layoutGridSize[0] * proxy.x()) + 'px',
|
||||
top: (layoutGridSize[1] * proxy.y()) + 'px',
|
||||
width: (layoutGridSize[0] * proxy.width()) + 'px',
|
||||
height: (layoutGridSize[1] * proxy.height()) + 'px'
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the top-left x coordinate, in grid space, of
|
||||
* this line's bounding box.
|
||||
@ -149,12 +163,6 @@ define(
|
||||
return handles;
|
||||
};
|
||||
|
||||
// Expose endpoint coordinates for editing
|
||||
proxy.editX1 = new AccessorMutator(element, 'x');
|
||||
proxy.editY1 = new AccessorMutator(element, 'y');
|
||||
proxy.editX2 = new AccessorMutator(element, 'x2');
|
||||
proxy.editY2 = new AccessorMutator(element, 'y2');
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
@ -35,21 +35,16 @@ define(
|
||||
* @memberof platform/features/layout
|
||||
* @constructor
|
||||
*/
|
||||
function ResizeHandle(element, minWidth, minHeight, gridSize) {
|
||||
function ResizeHandle(elementProxy, element) {
|
||||
this.elementProxy = elementProxy;
|
||||
this.element = element;
|
||||
|
||||
// Ensure reasonable defaults
|
||||
this.minWidth = minWidth || 0;
|
||||
this.minHeight = minHeight || 0;
|
||||
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
ResizeHandle.prototype.x = function (value) {
|
||||
var element = this.element;
|
||||
if (arguments.length > 0) {
|
||||
element.width = Math.max(
|
||||
this.minWidth,
|
||||
this.elementProxy.getMinWidth(),
|
||||
value - element.x
|
||||
);
|
||||
}
|
||||
@ -60,7 +55,7 @@ define(
|
||||
var element = this.element;
|
||||
if (arguments.length > 0) {
|
||||
element.height = Math.max(
|
||||
this.minHeight,
|
||||
this.elementProxy.getMinHeight(),
|
||||
value - element.y
|
||||
);
|
||||
}
|
||||
@ -68,7 +63,7 @@ define(
|
||||
};
|
||||
|
||||
ResizeHandle.prototype.getGridSize = function () {
|
||||
return this.gridSize;
|
||||
return this.elementProxy.getGridSize();
|
||||
};
|
||||
|
||||
return ResizeHandle;
|
||||
|
@ -24,9 +24,6 @@ define(
|
||||
['./TextProxy'],
|
||||
function (TextProxy) {
|
||||
|
||||
// Method names to expose from this proxy
|
||||
var HIDE = 'hideTitle', SHOW = 'showTitle';
|
||||
|
||||
/**
|
||||
* Selection proxy for telemetry elements in a fixed position view.
|
||||
*
|
||||
@ -45,24 +42,9 @@ define(
|
||||
function TelemetryProxy(element, index, elements, gridSize) {
|
||||
var proxy = new TextProxy(element, index, elements, gridSize);
|
||||
|
||||
// Toggle the visibility of the title
|
||||
function toggle() {
|
||||
// Toggle the state
|
||||
element.titled = !element.titled;
|
||||
|
||||
// Change which method is exposed, to influence
|
||||
// which button is shown in the toolbar
|
||||
delete proxy[SHOW];
|
||||
delete proxy[HIDE];
|
||||
proxy[element.titled ? HIDE : SHOW] = toggle;
|
||||
}
|
||||
|
||||
// Expose the domain object identifier
|
||||
proxy.id = element.id;
|
||||
|
||||
// Expose initial toggle
|
||||
proxy[element.titled ? HIDE : SHOW] = toggle;
|
||||
|
||||
// Don't expose text configuration
|
||||
delete proxy.text;
|
||||
|
||||
|
Reference in New Issue
Block a user