Initial functional view API implementation. See #1642

This commit is contained in:
Henry
2017-10-20 12:40:01 -07:00
parent c4cd36e15b
commit d7b44f8d09
8 changed files with 125 additions and 126 deletions

View File

@ -106,9 +106,9 @@ define([
* *
* @type {module:openmct.ViewRegistry} * @type {module:openmct.ViewRegistry}
* @memberof module:openmct.MCT# * @memberof module:openmct.MCT#
* @name mainViews * @name objectViews
*/ */
this.mainViews = new ViewRegistry(); this.objectViews = new ViewRegistry();
/** /**
* Registry for views which should appear in the Inspector area. * Registry for views which should appear in the Inspector area.
@ -255,6 +255,18 @@ define([
this.legacyExtension('types', legacyDefinition); this.legacyExtension('types', legacyDefinition);
}.bind(this)); }.bind(this));
this.objectViews.getAllProviders().forEach(function (p) {
this.legacyExtension('views', {
key: p.key,
provider: p,
name: p.name,
cssClass: p.cssClass,
description: p.description,
editable: p.editable,
template: '<mct-view mct-provider-key="' + p.key + '"/>'
});
}, this);
legacyRegistry.register('adapter', this.legacyBundle); legacyRegistry.register('adapter', this.legacyBundle);
legacyRegistry.enable('adapter'); legacyRegistry.enable('adapter');
/** /**

View File

@ -24,7 +24,6 @@ define([
'legacyRegistry', 'legacyRegistry',
'./actions/ActionDialogDecorator', './actions/ActionDialogDecorator',
'./capabilities/AdapterCapability', './capabilities/AdapterCapability',
'./controllers/AdaptedViewController',
'./directives/MCTView', './directives/MCTView',
'./services/Instantiate', './services/Instantiate',
'./services/MissingModelCompatibilityDecorator', './services/MissingModelCompatibilityDecorator',
@ -32,13 +31,11 @@ define([
'./policies/AdapterCompositionPolicy', './policies/AdapterCompositionPolicy',
'./policies/AdaptedViewPolicy', './policies/AdaptedViewPolicy',
'./runs/AlternateCompositionInitializer', './runs/AlternateCompositionInitializer',
'./runs/TimeSettingsURLHandler', './runs/TimeSettingsURLHandler'
'text!./templates/adapted-view-template.html'
], function ( ], function (
legacyRegistry, legacyRegistry,
ActionDialogDecorator, ActionDialogDecorator,
AdapterCapability, AdapterCapability,
AdaptedViewController,
MCTView, MCTView,
Instantiate, Instantiate,
MissingModelCompatibilityDecorator, MissingModelCompatibilityDecorator,
@ -46,15 +43,15 @@ define([
AdapterCompositionPolicy, AdapterCompositionPolicy,
AdaptedViewPolicy, AdaptedViewPolicy,
AlternateCompositionInitializer, AlternateCompositionInitializer,
TimeSettingsURLHandler, TimeSettingsURLHandler
adaptedViewTemplate
) { ) {
legacyRegistry.register('src/adapter', { legacyRegistry.register('src/adapter', {
"extensions": { "extensions": {
"directives": [ "directives": [
{ {
key: "mctView", key: "mctView",
implementation: MCTView implementation: MCTView,
depends: ["openmct"]
} }
], ],
capabilities: [ capabilities: [
@ -63,16 +60,6 @@ define([
implementation: AdapterCapability implementation: AdapterCapability
} }
], ],
controllers: [
{
key: "AdaptedViewController",
implementation: AdaptedViewController,
depends: [
'$scope',
'openmct'
]
}
],
services: [ services: [
{ {
key: "instantiate", key: "instantiate",
@ -135,12 +122,6 @@ define([
depends: ["openmct", "$location", "$rootScope"] depends: ["openmct", "$location", "$rootScope"]
} }
], ],
views: [
{
key: "adapted-view",
template: adaptedViewTemplate
}
],
licenses: [ licenses: [
{ {
"name": "almond", "name": "almond",

View File

@ -22,10 +22,12 @@
define([ define([
'./synchronizeMutationCapability', './synchronizeMutationCapability',
'./AlternateCompositionCapability' './AlternateCompositionCapability',
'./patchViewCapability'
], function ( ], function (
synchronizeMutationCapability, synchronizeMutationCapability,
AlternateCompositionCapability AlternateCompositionCapability,
patchViewCapability
) { ) {
/** /**
@ -46,6 +48,9 @@ define([
capabilities.mutation = capabilities.mutation =
synchronizeMutationCapability(capabilities.mutation); synchronizeMutationCapability(capabilities.mutation);
} }
if (capabilities.view) {
capabilities.view = patchViewCapability(capabilities.view);
}
if (AlternateCompositionCapability.appliesTo(model, id)) { if (AlternateCompositionCapability.appliesTo(model, id)) {
capabilities.composition = function (domainObject) { capabilities.composition = function (domainObject) {
return new AlternateCompositionCapability(this.$injector, domainObject); return new AlternateCompositionCapability(this.$injector, domainObject);

View File

@ -20,21 +20,41 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { define([
function AdaptedViewController($scope, openmct) { 'lodash'
function refresh(legacyObject) { ], function (
if (!legacyObject) { _
$scope.view = undefined; ) {
return;
function patchViewCapability(viewConstructor) {
return function makeCapability(domainObject) {
var capability = viewConstructor(domainObject);
var oldInvoke = capability.invoke.bind(capability);
capability.invoke = function () {
var availableViews = oldInvoke();
var newDomainObject = capability
.domainObject
.useCapability('adapter');
return _(availableViews).map(function (v, i) {
var vd = {
view: v,
priority: i + 100 // arbitrary to allow new views to
// be defaults by returning priority less than 100.
};
if (v.provider && v.provider.priority) {
vd.priority = v.provider.priority(newDomainObject);
}
return vd;
})
.sortBy('priority')
.map('view')
.value();
};
return capability;
};
} }
var domainObject = legacyObject.useCapability('adapter'); return patchViewCapability;
var providers = openmct.mainViews.get(domainObject);
$scope.view = providers[0] && providers[0].view(domainObject);
}
$scope.$watch('domainObject', refresh);
}
return AdaptedViewController;
}); });

View File

@ -21,19 +21,23 @@
*****************************************************************************/ *****************************************************************************/
define([ define([
'angular',
'./Region'
], function ( ], function (
angular,
Region
) { ) {
function MCTView() { function MCTView(openmct) {
return { return {
restrict: 'A', restrict: 'E',
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
var region = new Region(element[0]); var provider = openmct.objectViews.getByProviderKey(attrs.mctProviderKey);
scope.$watch(attrs.mctView, region.show.bind(region)); var view = new provider.view(scope.domainObject.useCapability('adapter'));
scope.$on("$destroy", region.clear.bind(region)); var domElement = element[0];
view.show(domElement);
if (view.destroy) {
scope.$on('$destroy', function () {
view.destroy(domElement);
});
}
} }
}; };
} }

View File

@ -1,45 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
function Region(element) {
this.activeView = undefined;
this.element = element;
}
Region.prototype.clear = function () {
if (this.activeView) {
this.activeView.destroy();
this.activeView = undefined;
}
};
Region.prototype.show = function (view) {
this.clear();
this.activeView = view;
if (this.activeView) {
this.activeView.show(this.element);
}
};
return Region;
});

View File

@ -29,9 +29,9 @@ define([], function () {
view, view,
legacyObject legacyObject
) { ) {
if (view.key === 'adapted-view') { if (view.hasOwnProperty('provider')) {
var domainObject = legacyObject.useCapability('adapter'); var domainObject = legacyObject.useCapability('adapter');
return this.openmct.mainViews.get(domainObject).length > 0; return view.provider.canView(domainObject);
} }
return true; return true;
}; };

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*global console */
define([], function () { define([], function () {
/** /**
@ -28,7 +29,7 @@ define([], function () {
* @memberof module:openmct * @memberof module:openmct
*/ */
function ViewRegistry() { function ViewRegistry() {
this.providers = []; this.providers = {};
} }
@ -39,11 +40,19 @@ define([], function () {
* which can provide views of this object * which can provide views of this object
*/ */
ViewRegistry.prototype.get = function (item) { ViewRegistry.prototype.get = function (item) {
return this.providers.filter(function (provider) { return this.getAllProviders()
.filter(function (provider) {
return provider.canView(item); return provider.canView(item);
}); });
}; };
/**
* @private
*/
ViewRegistry.prototype.getAllProviders = function () {
return Object.values(this.providers);
};
/** /**
* Register a new type of view. * Register a new type of view.
* *
@ -52,7 +61,22 @@ define([], function () {
* @memberof module:openmct.ViewRegistry# * @memberof module:openmct.ViewRegistry#
*/ */
ViewRegistry.prototype.addProvider = function (provider) { ViewRegistry.prototype.addProvider = function (provider) {
this.providers.push(provider); var key = provider.key;
if (key === undefined) {
throw "View providers must have a unique 'key' property defined";
}
if (this.providers[key] !== undefined) {
console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key);
}
this.providers[key] = provider;
};
/**
* @private
*/
ViewRegistry.prototype.getByProviderKey = function (key) {
return this.providers[key];
}; };
/** /**
@ -91,6 +115,12 @@ define([], function () {
* Exposes types of views in Open MCT. * Exposes types of views in Open MCT.
* *
* @interface ViewProvider * @interface ViewProvider
* @property {string} key a unique identifier for this view
* @property {string} name the human-readable name of this view
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of view
* @property {string} [cssClass] the CSS class to apply to labels for this
* view (to add icons, for instance)
* @memberof module:openmct * @memberof module:openmct
*/ */
@ -100,15 +130,28 @@ define([], function () {
* When called by Open MCT, this may include additional arguments * When called by Open MCT, this may include additional arguments
* which are on the path to the object to be viewed; for instance, * which are on the path to the object to be viewed; for instance,
* when viewing "A Folder" within "My Items", this method will be * when viewing "A Folder" within "My Items", this method will be
* invoked with "A Folder" (as a domain object) as the first argument, * invoked with "A Folder" (as a domain object) as the first argument
* and "My Items" as the second argument.
* *
* @method canView * @method canView
* @memberof module:openmct.ViewProvider# * @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object * @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed * to be viewed
* @returns {boolean} true if this domain object can be viewed using * @returns {boolean} 'true' if the view applies to the provided object,
* this provider * otherwise 'false'.
*/
/**
* Optional method determining the priority of a given view. If this
* function is not defined on a view provider, then a default priority
* of 100 will be applicable for all objects supported by this view.
*
* @method priority
* @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed
* @returns {number} The priority of the view. If multiple views could apply
* to an object, the view that returns the lowest number will be
* the default view.
*/ */
/** /**
@ -126,27 +169,6 @@ define([], function () {
* @returns {module:openmct.View} a view of this domain object * @returns {module:openmct.View} a view of this domain object
*/ */
/**
* Get metadata associated with this view provider. This may be used
* to populate the user interface with options associated with this
* view provider.
*
* @method metadata
* @memberof module:openmct.ViewProvider#
* @returns {module:openmct.ViewProvider~ViewMetadata} view metadata
*/
/**
* @typedef ViewMetadata
* @memberof module:openmct.ViewProvider~
* @property {string} name the human-readable name of this view
* @property {string} key a machine-readable name for this view
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of view
* @property {string} cssClass the CSS class to apply to labels for this
* view (to add icons, for instance)
*/
return ViewRegistry; return ViewRegistry;
}); });