Layout drawing (#2232)

* - Show "Add" button in the toolbar when a display layout object is selected.
- Add a flag to object view's show() method to indicate immediate selection of the view. If the view implements getSelectionContext() use it, otherwise set to default context.

* Create a component for each element.

* Saving work

* Add element factory for creating new instances of elements.

* Mutate element when a new one added and get elements when the component is mounted.

* Add create() method for creating a new telemetry and element in their respective view configuration.

* Add some of the toolbar controls for box, text, image and line elements. Also, add X, Y, Width and Height controls for alhpanumeric elements.

* Pass name to addElement as type.

* Add c-frame-inspectable class if item is inspectable.

* Clean up

* Hide frame for summary widgets by default.

* Better styling for editing

- s-selected on shell__main-container;
- Better edit grid coloring for espresso;

* - Update toolbar-button to support dialogs.
- Update toolbar to construct a value object based on form keys if a toolbar item has a dialogi, and mutate the form keys.
- Add toolbar controls for editing text and url for 'Text' and 'Image' elements respectively.

* Editing-related changes

- Removed hard-coded .is-selectable and .is-moveable from
LayoutItem.vue, updates accordingly to _global.scss;
- Theme constants updated;
- TODO: apply changes to Flexible Layouts;

* Better defaults

- Better default grid size and object size;

* - Fix toolbar-input to read value as a number if type is 'number'.
- Remove rawPosition from view configuration and instead get the position and dimensions from the properties (x, y, width and height) directly.
- Set the style property on the view configuration instead of the layout item.
- Move the logic for updating the style to the view configuration.

* Fix default dimensions for telemetry items and subobjects since the default grid size is changed.

* Remove form definition for display layout type.

* Reword the comment

* Let subobject view configuration handle new panel creation.

* Add default grid size back and remove unused code.

* Pass in only the needed method.

* Define default position in case the object is not added via drag 'n drop.
This commit is contained in:
Pegah Sarram 2018-12-04 09:12:45 -08:00 committed by Pete Richards
parent 32a0baa7a3
commit e07cfc9394
24 changed files with 1072 additions and 263 deletions

View File

@ -104,9 +104,9 @@ define([
} }
return (openmct.editor.isEditing() && return (openmct.editor.isEditing() &&
(selection[0] && selection[0].context.elementProxy && selection[0] && selection[0].context.elementProxy &&
selection[1] && selection[1].context.item.type === 'telemetry.fixed' || ((selection[1] && selection[1].context.item.type === 'telemetry.fixed') ||
selection[0] && selection[0].context.item.type === 'telemetry.fixed')); (selection[0] && selection[0].context.item && selection[0].context.item.type === 'telemetry.fixed')));
}, },
toolbar: function (selection) { toolbar: function (selection) {
var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"]; var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"];

View File

@ -28,27 +28,163 @@ define([], function () {
key: "layout", key: "layout",
description: "A toolbar for objects inside a display layout.", description: "A toolbar for objects inside a display layout.",
forSelection: function (selection) { forSelection: function (selection) {
// Apply the layout toolbar if the selected object is inside a layout, // Apply the layout toolbar if the edit mode is on, and the selected object
// and in edit mode. // is inside a layout, or the main layout is selected.
return (selection && return (openmct.editor.isEditing() && selection &&
selection[1] && ((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
selection[1].context.item && (selection[0].context.item && selection[0].context.item.type === 'layout')));
selection[1].context.item.type === 'layout' &&
openmct.editor.isEditing());
}, },
toolbar: function (selection) { toolbar: function (selection) {
let domainObject = selection[1].context.item; let selectedParent = selection[1] && selection[1].context.item,
let layoutItem = selection[0].context.layoutItem; selectedObject = selection[0].context.item,
layoutItem = selection[0].context.layoutItem,
toolbar = [];
if (layoutItem && layoutItem.type === 'telemetry-view') { if (selectedObject && selectedObject.type === 'layout') {
let path = "configuration.alphanumerics[" + layoutItem.config.alphanumeric.index + "]"; toolbar.push({
let metadata = openmct.telemetry.getMetadata(layoutItem.domainObject); control: "menu",
const TEXT_SIZE = [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96]; domainObject: selectedObject,
method: function (option) {
return [ selection[0].context.addElement(option.name.toLowerCase());
},
key: "add",
icon: "icon-plus",
label: "Add",
options: [
{ {
"name": "Box",
"class": "icon-box-round-corners"
},
{
"name": "Line",
"class": "icon-line-horz"
},
{
"name": "Text",
"class": "icon-font"
},
{
"name": "Image",
"class": "icon-image"
}
]
});
}
if (!layoutItem) {
return toolbar;
}
if (layoutItem.type === 'subobject-view') {
if (toolbar.length > 0) {
toolbar.push({
control: "separator"
});
}
toolbar.push({
control: "toggle-button",
domainObject: selectedParent,
property: "configuration.panels[" + layoutItem.id + "].hasFrame",
options: [
{
value: false,
icon: 'icon-frame-hide',
title: "Hide frame"
},
{
value: true,
icon: 'icon-frame-show',
title: "Show frame"
}
]
});
} else {
const TEXT_SIZE = [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96];
let path;
// TODO: get the path from the view configuration
// let path = layoutItem.config.path();
if (layoutItem.type === 'telemetry-view') {
path = "configuration.alphanumerics[" + layoutItem.config.alphanumeric.index + "]";
} else {
path = "configuration.elements[" + layoutItem.config.element.index + "]";
}
let separator = {
control: "separator"
},
fill = {
control: "color-picker",
domainObject: selectedParent,
property: path + ".fill",
icon: "icon-paint-bucket",
title: "Set fill color"
},
stroke = {
control: "color-picker",
domainObject: selectedParent,
property: path + ".stroke",
icon: "icon-line-horz",
title: "Set border color"
},
color = {
control: "color-picker",
domainObject: selectedParent,
property: path + ".color",
icon: "icon-font",
mandatory: true,
title: "Set text color",
preventNone: true
},
size = {
control: "select-menu", control: "select-menu",
domainObject: domainObject, domainObject: selectedParent,
property: path + ".size",
title: "Set text size",
options: TEXT_SIZE.map(size => {
return {
value: size + "px"
};
})
},
x = {
control: "input",
type: "number",
domainObject: selectedParent,
property: path + ".x",
label: "X:",
title: "X position"
},
y = {
control: "input",
type: "number",
domainObject: selectedParent,
property: path + ".y",
label: "Y:",
title: "Y position",
},
width = {
control: 'input',
type: 'number',
domainObject: selectedParent,
property: path + ".width",
label: 'W:',
title: 'Resize object width'
},
height = {
control: 'input',
type: 'number',
domainObject: selectedParent,
property: path + ".height",
label: 'H:',
title: 'Resize object height'
};
if (layoutItem.type === 'telemetry-view') {
// TODO: add "remove", "order", "useGrid"
let metadata = openmct.telemetry.getMetadata(layoutItem.domainObject),
displayMode = {
control: "select-menu",
domainObject: selectedParent,
property: path + ".displayMode", property: path + ".displayMode",
title: "Set display mode", title: "Set display mode",
options: [ options: [
@ -66,12 +202,9 @@ define([], function () {
} }
] ]
}, },
{ value = {
control: "separator"
},
{
control: "select-menu", control: "select-menu",
domainObject: domainObject, domainObject: selectedParent,
property: path + ".value", property: path + ".value",
title: "Set value", title: "Set value",
options: metadata.values().map(value => { options: metadata.values().map(value => {
@ -80,69 +213,114 @@ define([], function () {
value: value.key value: value.key
} }
}) })
},
{
control: "separator"
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".fill",
icon: "icon-paint-bucket",
title: "Set fill color"
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".stroke",
icon: "icon-line-horz",
title: "Set border color"
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".color",
icon: "icon-font",
mandatory: true,
title: "Set text color",
preventNone: true
},
{
control: "separator"
},
{
control: "select-menu",
domainObject: domainObject,
property: path + ".size",
title: "Set text size",
options: TEXT_SIZE.map(size => {
return {
value: size + "px"
}; };
}) toolbar = [
}, displayMode,
separator,
value,
separator,
fill,
stroke,
color,
separator,
size,
separator,
x,
y,
height,
width
]; ];
} else { } else if (layoutItem.type === 'text-view' ) {
return [ // TODO: Add "remove", "order", "useGrid"
let text = {
control: "button",
domainObject: selectedParent,
property: path,
icon: "icon-gear",
title: "Edit text properties",
dialog: {
name: "Text Element Properties",
sections: [
{ {
control: "toggle-button", rows: [
domainObject: domainObject,
property: "configuration.panels[" + layoutItem.id + "].hasFrame",
options: [
{ {
value: false, key: "text",
icon: 'icon-frame-hide', control: "textfield",
title: "Hide frame" name: "Text",
}, required: true
{
value: true,
icon: 'icon-frame-show',
title: "Show frame"
} }
] ]
} }
]; ]
} }
};
toolbar = [
fill,
stroke,
color,
separator,
size,
separator,
x,
y,
height,
width,
separator,
text
];
} else if (layoutItem.type === 'box-view') {
// TODO: Add "remove", "order", "useGrid"
toolbar = [
fill,
stroke,
separator,
x,
y,
height,
width
];
} else if (layoutItem.type === 'image-view') {
// TODO: Add "remove", "order", "useGrid"
let url = {
control: "button",
domainObject: selectedParent,
property: path,
icon: "icon-image",
title: "Edit image properties",
dialog: {
name: "Image Properties",
sections: [
{
rows: [
{
key: "url",
control: "textfield",
name: "Image URL",
"cssClass": "l-input-lg",
required: true
}
]
}
]
}
};
toolbar = [
stroke,
separator,
x,
y,
height,
width,
separator,
url
];
} else if (layoutItem.type === 'line-view') {
// TODO: Add "remove", "order", "useGrid", "x1", "y1", x2", "y2"
toolbar = [stroke];
}
}
return toolbar;
} }
} }
} }

View File

@ -30,7 +30,8 @@ define(function () {
domainObject.composition = []; domainObject.composition = [];
domainObject.configuration = { domainObject.configuration = {
panels: {}, panels: {},
alphanumerics: [] alphanumerics: [],
elements: []
}; };
} }
} }

View File

@ -0,0 +1,171 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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(
['./ViewConfiguration'],
function (ViewConfiguration) {
class ElementViewConfiguration extends ViewConfiguration {
static create(type, openmct) {
const DEFAULT_WIDTH = 10,
DEFAULT_HEIGHT = 5,
DEFAULT_X = 1,
DEFAULT_Y = 1;
const INITIAL_STATES = {
"image": {
stroke: "transparent"
},
"box": {
fill: "#717171",
stroke: "transparent"
},
"line": {
x: 5,
y: 3,
x2: 6,
y2: 6,
stroke: "#717171"
},
"text": {
fill: "transparent",
stroke: "transparent",
size: "13px",
color: ""
}
};
const DIALOGS = {
"image": {
name: "Image Properties",
sections: [
{
rows: [
{
key: "url",
control: "textfield",
name: "Image URL",
"cssClass": "l-input-lg",
required: true
}
]
}
]
},
"text": {
name: "Text Element Properties",
sections: [
{
rows: [
{
key: "text",
control: "textfield",
name: "Text",
required: true
}
]
}
]
}
};
let element = INITIAL_STATES[type] || {};
element = JSON.parse(JSON.stringify(element));
element.x = element.x || DEFAULT_X;
element.y = element.y || DEFAULT_Y;
element.width = DEFAULT_WIDTH;
element.height = DEFAULT_HEIGHT;
element.type = type;
return DIALOGS[type] ?
openmct.$injector.get('dialogService').getUserInput(DIALOGS[type], element) :
element;
}
/**
* @param {Object} configuration the element (line, box, text or image) view configuration
* @param {Object} configuration.element
* @param {Object} configuration.domainObject the telemetry domain object
* @param {Object} configuration.openmct the openmct object
*/
constructor({element, ...rest}) {
super(rest);
this.element = element;
this.updateStyle(this.position());
}
path() {
return "configuration.elements[" + this.element.index + "]";
}
x() {
return this.element.x;
}
y() {
return this.element.y;
}
width() {
return this.element.width;
}
height() {
return this.element.height;
}
observeProperties() {
[
"width",
"height",
"stroke",
"fill",
"x",
"y",
"x1",
"y1",
"x2",
"y2",
"color",
"size",
"text",
"url"
].forEach(property => {
this.attachListener(property, newValue => {
this.element[property] = newValue;
if (property === 'width' || property === 'height' ||
property === 'x' || property === 'y') {
this.updateStyle();
}
});
});
// TODO: attach listener for useGrid
}
inspectable() {
return false;
}
}
return ElementViewConfiguration;
}
);

View File

@ -25,28 +25,94 @@ define(
function (ViewConfiguration) { function (ViewConfiguration) {
class SubobjectViewConfiguration extends ViewConfiguration { class SubobjectViewConfiguration extends ViewConfiguration {
static create(domainObject, gridSize, position) {
const MINIMUM_FRAME_SIZE = [320, 180],
DEFAULT_DIMENSIONS = [10, 10],
DEFAULT_POSITION = [0, 0],
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
function getDefaultDimensions() {
return MINIMUM_FRAME_SIZE.map((min, index) => {
return Math.max(
Math.ceil(min / gridSize[index]),
DEFAULT_DIMENSIONS[index]
);
});
}
function hasFrameByDefault(type) {
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
}
position = position || DEFAULT_POSITION;
let defaultDimensions = getDefaultDimensions();
let panel = {
width: defaultDimensions[0],
height: defaultDimensions[1],
x: position[0],
y: position[1],
hasFrame: hasFrameByDefault(domainObject.type)
};
return panel;
}
/** /**
* *
* @param {Object} configuration the subobject view configuration * @param {Object} configuration the subobject view configuration
* @param {String} configuration.id the domain object keystring identifier * @param {String} configuration.id the domain object keystring identifier
* @param {Boolean} configuration.hasFrame flag to show/hide the frame * @param {Boolean} configuration.panel
* @param {Object} configuration.domainObject the domain object * @param {Object} configuration.domainObject the domain object to observe the changes on
* @param {Object} configuration.rawPosition an object that holds raw position and dimensions
* @param {Object} configuration.openmct the openmct object * @param {Object} configuration.openmct the openmct object
*/ */
constructor({id, hasFrame, ...rest}) { constructor({panel, id, ...rest}) {
super(rest); super(rest);
this.id = id; this.id = id;
this.hasFrame = hasFrame; this.panel = panel;
this.hasFrame = this.hasFrame.bind(this);
this.updateStyle(this.position());
} }
path() { path() {
return "configuration.panels[" + this.id + "]"; return "configuration.panels[" + this.id + "]";
} }
x() {
return this.panel.x;
}
y() {
return this.panel.y;
}
width() {
return this.panel.width;
}
height() {
return this.panel.height;
}
hasFrame() {
return this.panel.hasFrame;
}
observeProperties() { observeProperties() {
this.attachListener("hasFrame", newValue => { [
this.hasFrame = newValue; 'hasFrame',
'x',
'y',
'width',
'height'
].forEach(property => {
this.attachListener(property, newValue => {
this.panel[property] = newValue;
if (property === 'width' || property === 'height' ||
property === 'x' || property === 'y') {
this.updateStyle();
}
});
}); });
} }
} }

View File

@ -23,24 +23,77 @@
define( define(
['./ViewConfiguration'], ['./ViewConfiguration'],
function (ViewConfiguration) { function (ViewConfiguration) {
class TelemetryViewConfiguration extends ViewConfiguration { class TelemetryViewConfiguration extends ViewConfiguration {
static create(domainObject, position, openmct) {
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
function getDefaultTelemetryValue(domainObject, openmct) {
let metadata = openmct.telemetry.getMetadata(domainObject);
let valueMetadata = metadata.valuesForHints(['range'])[0];
if (valueMetadata === undefined) {
valueMetadata = metadata.values().filter(values => {
return !(values.hints.domain);
})[0];
}
if (valueMetadata === undefined) {
valueMetadata = metadata.values()[0];
}
return valueMetadata.key;
}
let alphanumeric = {
identifier: domainObject.identifier,
x: position[0],
y: position[1],
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
displayMode: 'all',
value: getDefaultTelemetryValue(domainObject, openmct),
stroke: "transparent",
fill: "",
color: "",
size: "13px",
};
return alphanumeric;
}
/** /**
* @param {Object} configuration the telemetry object view configuration * @param {Object} configuration the telemetry object view configuration
* @param {Object} configuration.alphanumeric * @param {Object} configuration.alphanumeric
* @param {Object} configuration.domainObject the telemetry domain object * @param {Object} configuration.domainObject the domain object to observe the changes on
* @param {Object} configuration.rawPosition an object that holds raw position and dimensions
* @param {Object} configuration.openmct the openmct object * @param {Object} configuration.openmct the openmct object
*/ */
constructor({alphanumeric, ...rest}) { constructor({alphanumeric, ...rest}) {
super(rest); super(rest);
this.alphanumeric = alphanumeric; this.alphanumeric = alphanumeric;
this.updateStyle(this.position());
} }
path() { path() {
return "configuration.alphanumerics[" + this.alphanumeric.index + "]"; return "configuration.alphanumerics[" + this.alphanumeric.index + "]";
} }
x() {
return this.alphanumeric.x;
}
y() {
return this.alphanumeric.y;
}
width() {
return this.alphanumeric.width;
}
height() {
return this.alphanumeric.height;
}
observeProperties() { observeProperties() {
[ [
'displayMode', 'displayMode',
@ -48,10 +101,19 @@ define(
'fill', 'fill',
'stroke', 'stroke',
'color', 'color',
'size' 'size',
'x',
'y',
'width',
'height'
].forEach(property => { ].forEach(property => {
this.attachListener(property, newValue => { this.attachListener(property, newValue => {
this.alphanumeric[property] = newValue; this.alphanumeric[property] = newValue;
if (property === 'width' || property === 'height' ||
property === 'x' || property === 'y') {
this.updateStyle();
}
}); });
}); });
} }

View File

@ -24,20 +24,23 @@
function () { function () {
class ViewConfiguration { class ViewConfiguration {
constructor({domainObject, openmct, rawPosition}) { constructor({domainObject, openmct, gridSize}) {
this.domainObject = domainObject; this.domainObject = domainObject;
this.rawPosition = rawPosition; this.gridSize = gridSize;
this.mutatePosition = this.mutatePosition.bind(this); this.mutatePosition = this.mutatePosition.bind(this);
this.listeners = []; this.listeners = [];
this.observe = openmct.objects.observe.bind(openmct.objects); this.observe = openmct.objects.observe.bind(openmct.objects);
this.mutate = function (path, value) { this.mutate = function (path, value) {
openmct.objects.mutate(this.domainObject, path, value); openmct.objects.mutate(this.domainObject, path, value);
}.bind(this); }.bind(this);
this.newPosition = {};
} }
mutatePosition() { mutatePosition() {
this.mutate(this.path() + ".dimensions", this.rawPosition.dimensions); this.mutate(this.path() + ".x", this.newPosition.position[0]);
this.mutate(this.path() + ".position", this.rawPosition.position); this.mutate(this.path() + ".y", this.newPosition.position[1]);
this.mutate(this.path() + ".width", this.newPosition.dimensions[0]);
this.mutate(this.path() + ".height", this.newPosition.dimensions[1]);
} }
attachListener(property, callback) { attachListener(property, callback) {
@ -58,13 +61,59 @@
this.listeners = []; this.listeners = [];
} }
position() {
return {
position: [this.x(), this.y()],
dimensions: [this.width(), this.height()]
};
}
path() { path() {
throw "NOT IMPLEMENTED;" throw "NOT IMPLEMENTED;"
} }
inspectable() {
return true;
}
updateStyle(raw) {
if (!raw) {
raw = this.position();
}
this.style = {
left: (this.gridSize[0] * raw.position[0]) + 'px',
top: (this.gridSize[1] * raw.position[1]) + 'px',
width: (this.gridSize[0] * raw.dimensions[0]) + 'px',
height: (this.gridSize[1] * raw.dimensions[1]) + 'px',
minWidth: (this.gridSize[0] * raw.dimensions[0]) + 'px',
minHeight: (this.gridSize[1] * raw.dimensions[1]) + 'px'
};
}
observeProperties() { observeProperties() {
// Not implemented // Not implemented
} }
x() {
// Not implemented
}
y() {
// Not implemented
}
width() {
// Not implemented
}
height() {
// Not implemented
}
hasFrame() {
// Not implemented
}
} }
return ViewConfiguration; return ViewConfiguration;

View File

@ -0,0 +1,63 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
<template>
<div class="c-box-view"
:style="styleObject">
</div>
</template>
<style lang="scss">
@import '~styles/sass-base';
.c-box-view {
display: flex;
align-items: stretch;
.c-frame & {
@include abs();
}
}
</style>
<script>
export default {
props: {
item: Object
},
computed: {
styleObject() {
let element = this.item.config.element;
return {
backgroundColor: element.fill,
border: '1px solid ' + element.stroke
}
}
},
mounted() {
this.item.config.attachListeners();
},
destroyed() {
this.item.config.removeListeners();
}
}
</script>

View File

@ -88,15 +88,9 @@
import LayoutItem from './LayoutItem.vue'; import LayoutItem from './LayoutItem.vue';
import TelemetryViewConfiguration from './../TelemetryViewConfiguration.js' import TelemetryViewConfiguration from './../TelemetryViewConfiguration.js'
import SubobjectViewConfiguration from './../SubobjectViewConfiguration.js' import SubobjectViewConfiguration from './../SubobjectViewConfiguration.js'
import ElementViewConfiguration from './../ElementViewConfiguration.js'
const DEFAULT_GRID_SIZE = [32, 32], const DEFAULT_GRID_SIZE = [10, 10];
DEFAULT_DIMENSIONS = [12, 8],
DEFAULT_TELEMETRY_DIMENSIONS = [2, 1],
DEFAULT_POSITION = [0, 0],
MINIMUM_FRAME_SIZE = [320, 180],
DEFAULT_HIDDEN_FRAME_TYPES = [
'hyperlink'
];
export default { export default {
data() { data() {
@ -119,25 +113,25 @@
this.makeTelemetryItem(alphanumeric, false); this.makeTelemetryItem(alphanumeric, false);
}); });
}, },
makeFrameItem(panel, initSelect) { getElements() {
let rawPosition = { let elements = this.newDomainObject.configuration.elements || [];
position: panel.position, elements.forEach((element, index) => {
dimensions: panel.dimensions element.index = index;
}; this.makeElementItem(element, false);
let style = this.convertPosition(rawPosition); });
},
makeSubobjectItem(panel, initSelect) {
let id = this.openmct.objects.makeKeyString(panel.domainObject.identifier); let id = this.openmct.objects.makeKeyString(panel.domainObject.identifier);
let config = new SubobjectViewConfiguration({ let config = new SubobjectViewConfiguration({
domainObject: this.newDomainObject, domainObject: this.newDomainObject,
panel: panel,
id: id, id: id,
hasFrame: panel.hasFrame, openmct: openmct,
rawPosition: rawPosition, gridSize: this.gridSize
openmct: openmct
}); });
this.layoutItems.push({ this.layoutItems.push({
id: id, id: id,
domainObject: panel.domainObject, domainObject: panel.domainObject,
style: style,
drilledIn: this.isItemDrilledIn(id), drilledIn: this.isItemDrilledIn(id),
initSelect: initSelect, initSelect: initSelect,
type: 'subobject-view', type: 'subobject-view',
@ -145,60 +139,35 @@
}); });
}, },
makeTelemetryItem(alphanumeric, initSelect) { makeTelemetryItem(alphanumeric, initSelect) {
let rawPosition = {
position: alphanumeric.position,
dimensions: alphanumeric.dimensions
};
let style = this.convertPosition(rawPosition);
let id = this.openmct.objects.makeKeyString(alphanumeric.identifier); let id = this.openmct.objects.makeKeyString(alphanumeric.identifier);
this.openmct.objects.get(id).then(domainObject => { this.openmct.objects.get(id).then(domainObject => {
let config = new TelemetryViewConfiguration({ let config = new TelemetryViewConfiguration({
domainObject: this.newDomainObject, domainObject: this.newDomainObject,
alphanumeric: alphanumeric, alphanumeric: alphanumeric,
rawPosition: rawPosition, openmct: openmct,
openmct: openmct gridSize: this.gridSize
}); });
this.layoutItems.push({ this.layoutItems.push({
id: id, id: id,
domainObject: domainObject, domainObject: domainObject,
style: style,
initSelect: initSelect, initSelect: initSelect,
alphanumeric: alphanumeric,
type: 'telemetry-view', type: 'telemetry-view',
config: config config: config
}); });
}); });
}, },
getSubobjectDefaultDimensions() { makeElementItem(element, initSelect) {
let gridSize = this.gridSize; let config = new ElementViewConfiguration({
return MINIMUM_FRAME_SIZE.map(function (min, i) { domainObject: this.newDomainObject,
return Math.max( element: element,
Math.ceil(min / gridSize[i]), openmct: openmct,
DEFAULT_DIMENSIONS[i] gridSize: this.gridSize
); });
this.layoutItems.push({
initSelect: initSelect,
type: element.type + '-view',
config: config
}); });
},
convertPosition(raw) {
return {
left: (this.gridSize[0] * raw.position[0]) + 'px',
top: (this.gridSize[1] * raw.position[1]) + 'px',
width: (this.gridSize[0] * raw.dimensions[0]) + 'px',
height: (this.gridSize[1] * raw.dimensions[1]) + 'px',
minWidth: (this.gridSize[0] * raw.dimensions[0]) + 'px',
minHeight: (this.gridSize[1] * raw.dimensions[1]) + 'px'
};
},
/**
* Checks if the frame should be hidden or not.
*
* @param type the domain object type
* @return {boolean} true if the object should have
* frame by default, false, otherwise
*/
hasFrameByDefault(type) {
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
}, },
setSelection(selection) { setSelection(selection) {
if (selection.length === 0) { if (selection.length === 0) {
@ -219,9 +188,8 @@
return this.drilledIn === id; return this.drilledIn === id;
}, },
updatePosition(item, newPosition) { updatePosition(item, newPosition) {
let newStyle = this.convertPosition(newPosition); item.config.newPosition = newPosition;
item.config.rawPosition = newPosition; item.config.updateStyle(newPosition);
item.style = newStyle;
}, },
bypassSelection($event) { bypassSelection($event) {
if (this.dragInProgress) { if (this.dragInProgress) {
@ -236,7 +204,7 @@
setTimeout(function () { setTimeout(function () {
this.dragInProgress = false; this.dragInProgress = false;
}.bind(this), 0); }.bind(this), 0);
// TODO: emit "finishResizing" for view components to mutate position?
item.config.mutatePosition(); item.config.mutatePosition();
}, },
mutate(path, value) { mutate(path, value) {
@ -278,39 +246,12 @@
} }
}, },
addAlphanumeric(domainObject, position) { addAlphanumeric(domainObject, position) {
let alphanumeric = {
identifier: domainObject.identifier,
position: position,
dimensions: DEFAULT_TELEMETRY_DIMENSIONS,
displayMode: 'all',
value: this.getDefaultTelemetryValue(domainObject),
stroke: "transparent",
fill: "",
color: "",
size: "13px",
};
let alphanumerics = this.newDomainObject.configuration.alphanumerics || []; let alphanumerics = this.newDomainObject.configuration.alphanumerics || [];
let alphanumeric = TelemetryViewConfiguration.create(domainObject, position, this.openmct);
alphanumeric.index = alphanumerics.push(alphanumeric) - 1; alphanumeric.index = alphanumerics.push(alphanumeric) - 1;
this.mutate("configuration.alphanumerics", alphanumerics); this.mutate("configuration.alphanumerics", alphanumerics);
this.makeTelemetryItem(alphanumeric, true); this.makeTelemetryItem(alphanumeric, true);
}, },
getDefaultTelemetryValue(domainObject) {
let metadata = this.openmct.telemetry.getMetadata(domainObject);
let valueMetadata = metadata.valuesForHints(['range'])[0];
if (valueMetadata === undefined) {
valueMetadata = metadata.values().filter(values => {
return !(values.hints.domain);
})[0];
}
if (valueMetadata === undefined) {
valueMetadata = metadata.values()[0];
}
return valueMetadata.key;
},
handleDragOver($event){ handleDragOver($event){
$event.preventDefault(); $event.preventDefault();
}, },
@ -322,7 +263,7 @@
return false; return false;
} }
}, },
addObject(domainObject) { addSubobject(domainObject) {
if (!this.isTelemetry(domainObject)) { if (!this.isTelemetry(domainObject)) {
let panels = this.newDomainObject.configuration.panels, let panels = this.newDomainObject.configuration.panels,
id = this.openmct.objects.makeKeyString(domainObject.identifier), id = this.openmct.objects.makeKeyString(domainObject.identifier),
@ -330,35 +271,29 @@
mutateObject = false, mutateObject = false,
initSelect = false; initSelect = false;
// If this is a new panel, select it and save the configuration. // If the panel doesn't exist, create one and mutate the configuration
if (!panel) { if (!panel) {
panel = {}; panel = SubobjectViewConfiguration.create(domainObject, this.gridSize, this.droppedObjectPosition);
mutateObject = true;
initSelect = true; initSelect = true;
}
panel.dimensions = panel.dimensions || this.getSubobjectDefaultDimensions();
panel.hasFrame = panel.hasOwnProperty('hasFrame') ?
panel.hasFrame :
this.hasFrameByDefault(domainObject.type);
if (this.droppedObjectPosition) {
panel.position = this.droppedObjectPosition;
this.droppedObjectPosition = undefined;
} else {
panel.position = panel.position || DEFAULT_POSITION;
}
if (mutateObject) {
this.mutate("configuration.panels[" + id + "]", panel); this.mutate("configuration.panels[" + id + "]", panel);
delete this.droppedObjectPosition;
} }
panel.domainObject = domainObject; panel.domainObject = domainObject;
this.makeFrameItem(panel, initSelect); this.makeSubobjectItem(panel, initSelect);
} }
}, },
removeObject() { removeSubobject() {
// Not yet implemented
},
addElement(type) {
let elements = this.newDomainObject.configuration.elements || [];
Promise.resolve(ElementViewConfiguration.create(type, this.openmct))
.then(element => {
element.index = elements.push(element) - 1;
this.mutate("configuration.elements", elements);
this.makeElementItem(element, true);
});
} }
}, },
mounted() { mounted() {
@ -373,15 +308,16 @@
this.openmct.selection.on('change', this.setSelection); this.openmct.selection.on('change', this.setSelection);
this.composition = this.openmct.composition.get(this.newDomainObject); this.composition = this.openmct.composition.get(this.newDomainObject);
this.composition.on('add', this.addObject); this.composition.on('add', this.addSubobject);
this.composition.on('remove', this.removeObject); this.composition.on('remove', this.removeSubobject);
this.composition.load(); this.composition.load();
this.getAlphanumerics(); this.getAlphanumerics();
this.getElements();
}, },
destroyed: function () { destroyed: function () {
this.openmct.off('change', this.setSelection); this.openmct.off('change', this.setSelection);
this.composition.off('add', this.addObject); this.composition.off('add', this.addSubobject);
this.composition.off('remove', this.removeObject); this.composition.off('remove', this.removeSubobject);
this.unlisten(); this.unlisten();
} }
} }

View File

@ -0,0 +1,65 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
<template>
<div class="c-image-view"
:style="styleObject">
</div>
</template>
<style lang="scss">
@import '~styles/sass-base';
.c-image-view {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
.c-frame & {
@include abs();
border: 1px solid transparent;
}
}
</style>
<script>
export default {
props: {
item: Object
},
computed: {
styleObject() {
let element = this.item.config.element;
return {
backgroundImage: 'url(' + element.url + ')',
border: '1px solid ' + element.stroke
}
}
},
mounted() {
this.item.config.attachListeners();
},
destroyed() {
this.item.config.removeListeners();
}
}
</script>

View File

@ -21,12 +21,14 @@
*****************************************************************************/ *****************************************************************************/
<template> <template>
<div class="c-frame has-local-controls is-selectable is-moveable" <div class="c-frame has-local-controls"
:style="item.style" :style="item.config.style"
:class="classObject" :class="[classObject, {
'u-inspectable': item.config.inspectable()
}]"
@dblclick="drill(item.id, $event)"> @dblclick="drill(item.id, $event)">
<component :is="item.type" :item="item"></component> <component :is="item.type" :item="item" :gridSize="gridSize"></component>
<!-- Drag handles --> <!-- Drag handles -->
<div class="c-frame-edit"> <div class="c-frame-edit">
@ -72,6 +74,10 @@
<script> <script>
import SubobjectView from './SubobjectView.vue' import SubobjectView from './SubobjectView.vue'
import TelemetryView from './TelemetryView.vue' import TelemetryView from './TelemetryView.vue'
import BoxView from './BoxView.vue'
import TextView from './TextView.vue'
import LineView from './LineView.vue'
import ImageView from './ImageView.vue'
import LayoutDrag from './../LayoutDrag' import LayoutDrag from './../LayoutDrag'
export default { export default {
@ -82,13 +88,17 @@
}, },
components: { components: {
SubobjectView, SubobjectView,
TelemetryView TelemetryView,
BoxView,
TextView,
LineView,
ImageView
}, },
computed: { computed: {
classObject: function () { classObject: function () {
return { return {
'is-drilled-in': this.item.drilledIn, 'is-drilled-in': this.item.drilledIn,
'no-frame': !this.item.config.hasFrame 'no-frame': !this.item.config.hasFrame()
} }
} }
}, },
@ -102,6 +112,10 @@
return; return;
} }
if (!this.item.domainObject) {
return;
}
if (this.openmct.composition.get(this.item.domainObject) === undefined) { if (this.openmct.composition.get(this.item.domainObject) === undefined) {
return; return;
} }
@ -126,7 +140,7 @@
this.updatePosition(event); this.updatePosition(event);
this.activeDrag = new LayoutDrag( this.activeDrag = new LayoutDrag(
this.item.config.rawPosition, this.item.config.position(),
posFactor, posFactor,
dimFactor, dimFactor,
this.gridSize this.gridSize
@ -157,7 +171,8 @@
mounted() { mounted() {
let context = { let context = {
item: this.item.domainObject, item: this.item.domainObject,
layoutItem: this.item layoutItem: this.item,
addElement: this.$parent.addElement
}; };
this.removeSelectable = this.openmct.selection.selectable( this.removeSelectable = this.openmct.selection.selectable(

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
<template>
<div>
<svg :width="gridSize[0] * element.width"
:height=" gridSize[1] * element.height">
<line :x1=" gridSize[0] * element.x1 + 1"
:y1="gridSize[1] * element.y1 + 1 "
:x2="gridSize[0] * element.x2 + 1"
:y2=" gridSize[1] * element.y2 + 1 "
:stroke="element.stroke"
stroke-width="2">
</line>
</svg>
</div>
</template>
<script>
export default {
props: {
item: Object,
gridSize: Array
},
computed: {
element() {
return this.item.config.element;
}
},
mounted() {
this.item.config.attachListeners();
},
destroyed() {
this.item.config.removeListeners();
}
}
</script>

View File

@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
<template>
<div class="c-text-view"
:style="styleObject">
{{ item.config.element.text }}
</div>
</template>
<style lang="scss">
@import '~styles/sass-base';
.c-text-view {
display: flex;
align-items: stretch;
.c-frame & {
@include abs();
border: 1px solid transparent;
}
}
</style>
<script>
export default {
props: {
item: Object
},
computed: {
styleObject() {
let element = this.item.config.element;
return {
backgroundColor: element.fill,
borderColor: element.stroke,
color: element.color,
fontSize: element.size
}
}
},
mounted() {
this.item.config.attachListeners();
},
destroyed() {
this.item.config.removeListeners();
}
}
</script>

View File

@ -41,7 +41,7 @@ export default function () {
components: { components: {
Layout Layout
}, },
template: '<layout :domain-object="domainObject"></layout>', template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>',
provide: { provide: {
openmct, openmct,
objectUtils objectUtils
@ -54,6 +54,12 @@ export default function () {
} }
}); });
}, },
getSelectionContext() {
return {
item: domainObject,
addElement: component && component.$refs.displayLayout.addElement
}
},
destroy() { destroy() {
component.$destroy(); component.$destroy();
} }

View File

@ -92,12 +92,13 @@ $colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
// Base Colors // Base Colors
$dlSpread: 20%; $dlSpread: 20%;
$editColor: #00c7c3; $editColor: #00c7c3;
$editColorBg: darken($editColor, $dlSpread); $editColorBgBase: darken($editColor, $dlSpread);
$editColorBg: rgba($editColorBgBase, 0.2);
$editColorFg: lighten($editColor, $dlSpread); $editColorFg: lighten($editColor, $dlSpread);
$editColorHov: lighten($editColor, 20%); $editColorHov: lighten($editColor, 20%);
// Canvas // Canvas
$editCanvasColorBg: #002524; $editCanvasColorBg: $editColorBg; //#002524;
$editCanvasColorGrid: darken($editCanvasColorBg, 2%); $editCanvasColorGrid: rgba($editColorBgBase, 0.4); //lighten($editCanvasColorBg, 3%);
// Selectable // Selectable
$editSelectableColor: #006563; $editSelectableColor: #006563;
$editSelectableColorFg: lighten($editSelectableColor, 20%); $editSelectableColorFg: lighten($editSelectableColor, 20%);

View File

@ -92,6 +92,7 @@ $colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
// Base Colors // Base Colors
$dlSpread: 20%; $dlSpread: 20%;
$editColor: #00c7c3; $editColor: #00c7c3;
$editColorBgBase: darken($editColor, $dlSpread);
$editColorBg: darken($editColor, $dlSpread); $editColorBg: darken($editColor, $dlSpread);
$editColorFg: lighten($editColor, $dlSpread); $editColorFg: lighten($editColor, $dlSpread);
$editColorHov: lighten($editColor, 20%); $editColorHov: lighten($editColor, 20%);
@ -99,9 +100,9 @@ $editColorHov: lighten($editColor, 20%);
$editCanvasColorBg: #e6ffff; $editCanvasColorBg: #e6ffff;
$editCanvasColorGrid: darken($editCanvasColorBg, 10%); $editCanvasColorGrid: darken($editCanvasColorBg, 10%);
// Selectable // Selectable
$editSelectableColor: darken($colorBodyBg, 10%); $editSelectableColor: #acdad6;
$editSelectableColorFg: darken($editSelectableColor, 20%); $editSelectableColorFg: darken($editSelectableColor, 20%);
$editSelectableColorHov: darken($editSelectableColor, 10%); //darken($colorBodyBg, 20%); $editSelectableColorHov: darken($editSelectableColor, 10%);
// Selectable selected // Selectable selected
$editSelectableColorSelected: $editColor; //$editSelectableColorHov; $editSelectableColorSelected: $editColor; //$editSelectableColorHov;
$editSelectableColorSelectedFg: lighten($editSelectableColorSelected, 50%); $editSelectableColorSelectedFg: lighten($editSelectableColorSelected, 50%);
@ -111,6 +112,7 @@ $editSelectableBorderHov: 1px dotted $editColor;
$editSelectableBorderSelected: 1px solid $editColor; $editSelectableBorderSelected: 1px solid $editColor;
$editMoveableSelectedShdw: rgba($editColor, 0.5) 0 0 10px; $editMoveableSelectedShdw: rgba($editColor, 0.5) 0 0 10px;
$editBorderDrilledIn: 1px dashed #9971ff; $editBorderDrilledIn: 1px dashed #9971ff;
$colorGridLines: rgba($editColor, 0.2);
/************************************************** BROWSING */ /************************************************** BROWSING */
$browseBorderSelectableHov: 1px dotted rgba($colorBodyFg, 0.2); $browseBorderSelectableHov: 1px dotted rgba($colorBodyFg, 0.2);

View File

@ -240,7 +240,7 @@ body.desktop .has-local-controls {
} }
/*************************** SELECTION */ /*************************** SELECTION */
.is-selectable { .u-inspectable {
&:hover { &:hover {
box-shadow: $browseShdwSelectableHov; box-shadow: $browseShdwSelectableHov;
} }
@ -248,7 +248,7 @@ body.desktop .has-local-controls {
/**************************** EDITING */ /**************************** EDITING */
.is-editing { .is-editing {
*:not(.is-drilled-in).is-selectable { *:not(.is-drilled-in).c-frame {
border: $editSelectableBorder; border: $editSelectableBorder;
&:hover { &:hover {
@ -269,7 +269,7 @@ body.desktop .has-local-controls {
border: $editBorderDrilledIn; border: $editBorderDrilledIn;
} }
*[s-selected] .is-moveable { *[s-selected] {
cursor: move; cursor: move;
} }
@ -350,6 +350,14 @@ body.desktop .has-local-controls {
background: $editCanvasColorBg; background: $editCanvasColorBg;
} }
.l-shell__main-container {
box-shadow: $colorBodyBg 0 0 0 1px, rgba($editColor, 0.7) 0 0 0 2px;
&[s-selected] {
// Provide a clearer selection context articulation
box-shadow: $colorBodyBg 0 0 0 1px, $editColor 0 0 0px 3px;
}
}
// Layouts // Layouts
[s-selected], [s-selected],
[s-selected-parent] { [s-selected-parent] {

View File

@ -70,11 +70,14 @@ export default {
if (this.mutationUnobserver) { if (this.mutationUnobserver) {
this.mutationUnobserver(); this.mutationUnobserver();
} }
if (this.parentObject) {
this.mutationUnobserver = this.openmct.objects.observe(this.parentObject, '*', (updatedModel) => { this.mutationUnobserver = this.openmct.objects.observe(this.parentObject, '*', (updatedModel) => {
this.parentObject = updatedModel; this.parentObject = updatedModel;
this.refreshComposition(); this.refreshComposition();
}); });
this.refreshComposition(); this.refreshComposition();
}
}, },
refreshComposition() { refreshComposition() {
let composition = this.openmct.composition.get(this.parentObject); let composition = this.openmct.composition.get(this.parentObject);

View File

@ -41,7 +41,7 @@ export default {
delete this.viewContainer; delete this.viewContainer;
delete this.currentView; delete this.currentView;
}, },
updateView() { updateView(immediatelySelect) {
this.clear(); this.clear();
if (!this.currentObject) { if (!this.currentObject) {
return; return;
@ -58,18 +58,34 @@ export default {
} }
this.currentView = provider.view(this.currentObject); this.currentView = provider.view(this.currentObject);
this.currentView.show(this.viewContainer); this.currentView.show(this.viewContainer);
if (immediatelySelect) {
this.removeSelectable = openmct.selection.selectable(
this.$el,
this.currentView.getSelectionContext ?
this.currentView.getSelectionContext() :
{ item: this.currentObject },
true
);
}
}, },
show(object, viewKey) { show(object, viewKey, immediatelySelect) {
if (this.unlisten) { if (this.unlisten) {
this.unlisten(); this.unlisten();
} }
if (this.removeSelectable) {
this.removeSelectable();
delete this.removeSelectable;
}
this.currentObject = object; this.currentObject = object;
this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => { this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => {
this.currentObject = mutatedObject; this.currentObject = mutatedObject;
}); });
this.viewKey = viewKey; this.viewKey = viewKey;
this.updateView(); this.updateView(immediatelySelect);
}, },
onDragOver(event) { onDragOver(event) {
event.preventDefault(); event.preventDefault();

View File

@ -46,18 +46,33 @@
} }
let structure = this.openmct.toolbars.get(selection) || []; let structure = this.openmct.toolbars.get(selection) || [];
this.structure = structure.map(function (item) { this.structure = structure.map(item => {
let toolbarItem = {...item}; let toolbarItem = {...item};
let domainObject = toolbarItem.domainObject; let domainObject = toolbarItem.domainObject;
let formKeys = [];
toolbarItem.control = "toolbar-" + toolbarItem.control; toolbarItem.control = "toolbar-" + toolbarItem.control;
if (toolbarItem.dialog) {
toolbarItem.dialog.sections.forEach(section => {
section.rows.forEach(row => {
formKeys.push(row.key);
})
});
toolbarItem.formKeys = formKeys;
}
if (domainObject) { if (domainObject) {
if (formKeys.length > 0) {
toolbarItem.value = this.getFormValue(domainObject, toolbarItem);
} else {
toolbarItem.value = _.get(domainObject, item.property); toolbarItem.value = _.get(domainObject, item.property);
}
this.registerListener(domainObject); this.registerListener(domainObject);
} }
return toolbarItem; return toolbarItem;
}.bind(this)); });
}, },
registerListener(domainObject) { registerListener(domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier); let id = this.openmct.objects.makeKeyString(domainObject.identifier);
@ -85,7 +100,7 @@
setTimeout(this.updateToolbarAfterMutation.bind(this)); setTimeout(this.updateToolbarAfterMutation.bind(this));
}, },
updateToolbarAfterMutation() { updateToolbarAfterMutation() {
this.structure = this.structure.map((item) => { this.structure = this.structure.map(item => {
let toolbarItem = {...item}; let toolbarItem = {...item};
let domainObject = toolbarItem.domainObject; let domainObject = toolbarItem.domainObject;
@ -95,10 +110,11 @@
if (newObject) { if (newObject) {
toolbarItem.domainObject = newObject; toolbarItem.domainObject = newObject;
let newValue = _.get(newObject, item.property);
if (toolbarItem.value !== newValue) { if (toolbarItem.formKeys) {
toolbarItem.value = newValue; toolbarItem.value = this.getFormValue(newObject, toolbarItem);
} else {
toolbarItem.value = _.get(newObject, item.property);
} }
} }
} }
@ -114,6 +130,13 @@
}); });
this.toolbarUpdateScheduled = false; this.toolbarUpdateScheduled = false;
}, },
getFormValue(domainObject, toolbarItem) {
let value = {};
toolbarItem.formKeys.map(key => {
value[key] = _.get(domainObject, toolbarItem.property + "." + key);
});
return value;
},
removeListeners() { removeListeners() {
if (this.unObserveObjects) { if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => { this.unObserveObjects.forEach((unObserveObject) => {
@ -138,7 +161,20 @@
return toolbarItem; return toolbarItem;
}); });
// If value is an object, iterate the toolbar structure and mutate all keys in form.
// Otherwise, mutate the property.
if (value === Object(value)) {
this.structure.map(s => {
if (s.formKeys) {
s.formKeys.forEach(key => {
this.openmct.objects.mutate(item.domainObject, item.property + "." + key, value[key]);
});
}
});
} else {
this.openmct.objects.mutate(item.domainObject, item.property, value); this.openmct.objects.mutate(item.domainObject, item.property, value);
}
}, },
triggerMethod(item, event) { triggerMethod(item, event) {
if (item.method) { if (item.method) {

View File

@ -18,13 +18,18 @@
<script> <script>
export default { export default {
inject: ['openmct'],
props: { props: {
options: Object options: Object
}, },
methods: { methods: {
onClick(event) { onClick(event) {
if (this.options.dialog) { if (this.options.dialog) {
// TODO: display a dialog this.openmct.$injector.get('dialogService')
.getUserInput(this.options.dialog, this.options.value)
.then(value => {
this.$emit('change', {...value}, this.options);
});
} }
this.$emit('click', this.options); this.$emit('click', this.options);
} }

View File

@ -33,7 +33,13 @@ export default {
}, },
methods: { methods: {
onChange(event) { onChange(event) {
this.$emit('change', event.target.value, this.options); let value = event.target.value;
if (this.options.type === 'number') {
value = event.target.valueAsNumber;
}
this.$emit('change', value, this.options);
} }
} }
} }

View File

@ -131,6 +131,16 @@ define([], function () {
* @memberof module:openmct.View# * @memberof module:openmct.View#
*/ */
/**
* Returns the selection context.
*
* View implementations should use this method to customize
* the selection context.
*
* @method getSelectionContext
* @memberof module:openmct.View#
*/
/** /**
* Exposes types of views in Open MCT. * Exposes types of views in Open MCT.
* *

View File

@ -7,24 +7,11 @@ define([
return function install(openmct) { return function install(openmct) {
let navigateCall = 0; let navigateCall = 0;
let browseObject; let browseObject;
let removeSelectable = undefined;
function viewObject(object, viewProvider) { function viewObject(object, viewProvider) {
if (removeSelectable) { openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
removeSelectable();
removeSelectable = undefined;
}
openmct.layout.$refs.browseObject.show(object, viewProvider.key);
openmct.layout.$refs.browseBar.domainObject = object; openmct.layout.$refs.browseBar.domainObject = object;
openmct.layout.$refs.browseBar.viewKey = viewProvider.key; openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
removeSelectable = openmct.selection.selectable(
openmct.layout.$refs.browseObject.$el,
{
item: object
},
true
);
}; };
function navigateToPath(path, currentViewKey) { function navigateToPath(path, currentViewKey) {