diff --git a/index.html b/index.html index 5ce5e11dfe..0525853be0 100644 --- a/index.html +++ b/index.html @@ -49,6 +49,7 @@ openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.ImportExport()); + openmct.install(openmct.plugins.FixedView()); openmct.install(openmct.plugins.AutoflowView({ type: "telemetry.panel" })); diff --git a/platform/features/fixed/bundle.js b/platform/features/fixed/bundle.js deleted file mode 100644 index ce53af9dac..0000000000 --- a/platform/features/fixed/bundle.js +++ /dev/null @@ -1,449 +0,0 @@ -/***************************************************************************** - * 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([ - "./src/FixedController", - "./templates/fixed.html", - "./templates/frame.html", - "./templates/elements/telemetry.html", - "./templates/elements/box.html", - "./templates/elements/line.html", - "./templates/elements/text.html", - "./templates/elements/image.html", - "legacyRegistry" -], function ( - FixedController, - fixedTemplate, - frameTemplate, - telemetryTemplate, - boxTemplate, - lineTemplate, - textTemplate, - imageTemplate, - legacyRegistry -) { - - legacyRegistry.register("platform/features/fixed", { - "name": "Fixed position components.", - "description": "Plug in adding Fixed Position object type.", - "extensions": { - "views": [ - { - "key": "fixed-display", - "name": "Fixed Position Display", - "cssClass": "icon-box-with-dashed-lines", - "type": "telemetry.fixed", - "template": fixedTemplate, - "uses": ["composition"], - "editable": true - } - ], - "templates": [ - { - "key": "fixed.telemetry", - "template": telemetryTemplate - }, - { - "key": "fixed.box", - "template": boxTemplate - }, - { - "key": "fixed.line", - "template": lineTemplate - }, - { - "key": "fixed.text", - "template": textTemplate - }, - { - "key": "fixed.image", - "template": imageTemplate - } - ], - "controllers": [ - { - "key": "FixedController", - "implementation": FixedController, - "depends": [ - "$scope", - "$q", - "dialogService", - "openmct", - "$element" - ] - } - ], - - "toolbars": [ - { - name: "Fixed Position Toolbar", - key: "fixed.position", - description: "Toolbar for the selected element inside a fixed position display.", - forSelection: function (selection) { - if (!selection) { - return; - } - - return ( - selection[0] && selection[0].context.elementProxy && - selection[1] && selection[1].context.item.type === 'telemetry.fixed' || - selection[0] && selection[0].context.item.type === 'telemetry.fixed' - ); - }, - toolbar: function (selection) { - var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"]; - var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"]; - var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"]; - var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"]; - var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"]; - var fixedPageProperties = ["add"]; - - var properties = [], - fixedItem = selection[0] && selection[0].context.item, - elementProxy = selection[0] && selection[0].context.elementProxy, - domainObject = selection[1] && selection[1].context.item, - path; - - if (elementProxy) { - var type = elementProxy.element.type; - path = "configuration['fixed-display'].elements[" + elementProxy.index + "]"; - properties = - type === 'fixed.image' ? imageProperties : - type === 'fixed.text' ? textProperties : - type === 'fixed.box' ? boxProperties : - type === 'fixed.line' ? lineProperties : - type === 'fixed.telemetry' ? telemetryProperties : []; - } else if (fixedItem) { - properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties; - } - - return [ - { - control: "menu-button", - domainObject: domainObject || selection[0].context.item, - method: function (value) { - selection[0].context.fixedController.add(value); - }, - key: "add", - cssClass: "icon-plus", - text: "Add", - options: [ - { - "name": "Box", - "cssClass": "icon-box", - "key": "fixed.box" - }, - { - "name": "Line", - "cssClass": "icon-line-horz", - "key": "fixed.line" - }, - { - "name": "Text", - "cssClass": "icon-T", - "key": "fixed.text" - }, - { - "name": "Image", - "cssClass": "icon-image", - "key": "fixed.image" - } - ] - }, - { - control: "menu-button", - domainObject: domainObject, - method: function (value) { - selection[0].context.fixedController.order( - selection[0].context.elementProxy, - value - ); - }, - key: "order", - cssClass: "icon-layers", - title: "Layering", - description: "Move the selected object above or below other objects", - options: [ - { - "name": "Move to Top", - "cssClass": "icon-arrow-double-up", - "key": "top" - }, - { - "name": "Move Up", - "cssClass": "icon-arrow-up", - "key": "up" - }, - { - "name": "Move Down", - "cssClass": "icon-arrow-down", - "key": "down" - }, - { - "name": "Move to Bottom", - "cssClass": "icon-arrow-double-down", - "key": "bottom" - } - ] - }, - { - control: "color", - domainObject: domainObject, - property: path + ".fill", - cssClass: "icon-paint-bucket", - title: "Fill color", - description: "Set fill color", - key: 'fill' - }, - { - control: "color", - domainObject: domainObject, - property: path + ".stroke", - cssClass: "icon-line-horz", - title: "Border color", - description: "Set border color", - key: 'stroke' - }, - { - control: "dialog-button", - domainObject: domainObject, - property: path + ".url", - cssClass: "icon-image", - title: "Image Properties", - description: "Edit image properties", - key: 'url', - dialog: { - control: "textfield", - name: "Image URL", - cssClass: "l-input-lg", - required: true - } - }, - { - control: "color", - domainObject: domainObject, - property: path + ".color", - cssClass: "icon-T", - title: "Text color", - mandatory: true, - description: "Set text color", - key: 'color' - }, - { - control: "select", - domainObject: domainObject, - property: path + ".size", - title: "Text size", - description: "Set text size", - "options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) { - return { "name": size + " px", "value": size + "px" }; - }), - key: 'size' - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".x", - text: "X", - name: "X", - key: "x", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".y", - text: "Y", - name: "Y", - key: "y", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".x", - text: "X1", - name: "X1", - key: "x1", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".y", - text: "Y1", - name: "Y1", - key: "y1", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".x2", - text: "X2", - name: "X2", - key: "x2", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".y2", - text: "Y2", - name: "Y2", - key: "y2", - cssClass: "l-input-sm", - min: "0" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".height", - text: "H", - name: "H", - key: "height", - cssClass: "l-input-sm", - description: "Resize object height", - min: "1" - }, - { - control: "numberfield", - domainObject: domainObject, - property: path + ".width", - text: "W", - name: "W", - key: "width", - cssClass: "l-input-sm", - description: "Resize object width", - min: "1" - }, - { - control: "checkbox", - domainObject: domainObject, - property: path + ".useGrid", - name: "Snap to Grid", - key: "useGrid" - }, - { - control: "dialog-button", - domainObject: domainObject, - property: path + ".text", - cssClass: "icon-gear", - title: "Text Properties", - description: "Edit text properties", - key: "text", - dialog: { - control: "textfield", - name: "Text", - required: true - } - }, - { - control: "checkbox", - domainObject: domainObject, - property: path + ".titled", - name: "Show Title", - key: "titled" - }, - { - control: "button", - domainObject: domainObject, - method: function () { - selection[0].context.fixedController.remove( - selection[0].context.elementProxy - ); - }, - key: "remove", - cssClass: "icon-trash" - } - ].filter(function (item) { - var filtered; - - properties.forEach(function (property) { - if (item.property && item.key === property || - item.method && item.key === property) { - filtered = item; - } - }); - - return filtered; - }); - } - } - ], - "types": [ - { - "key": "telemetry.fixed", - "name": "Fixed Position Display", - "cssClass": "icon-box-with-dashed-lines", - "description": "Collect and display telemetry elements in " + - "alphanumeric format in a simple canvas workspace. " + - "Elements can be positioned and sized. " + - "Lines, boxes and images can be added as well.", - "priority": 899, - "delegates": [ - "telemetry" - ], - "features": "creation", - "contains": [ - { - "has": "telemetry" - } - ], - "model": { - "layoutGrid": [64, 16], - "composition": [] - }, - "properties": [ - { - "name": "Layout Grid", - "control": "composite", - "items": [ - { - "name": "Horizontal grid (px)", - "control": "textfield", - "cssClass": "l-input-sm l-numeric" - }, - { - "name": "Vertical grid (px)", - "control": "textfield", - "cssClass": "l-input-sm l-numeric" - } - ], - "pattern": "^(\\d*[1-9]\\d*)?$", - "property": "layoutGrid", - "conversion": "number[]" - } - ], - "views": [ - "fixed-display" - ] - } - ] - } - }); -}); diff --git a/platform/features/fixed/plugin.js b/platform/features/fixed/plugin.js new file mode 100644 index 0000000000..1e6c652285 --- /dev/null +++ b/platform/features/fixed/plugin.js @@ -0,0 +1,475 @@ +/***************************************************************************** + * 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([ + "./src/FixedController", + "./templates/fixed.html", + "./templates/frame.html", + "./templates/elements/telemetry.html", + "./templates/elements/box.html", + "./templates/elements/line.html", + "./templates/elements/text.html", + "./templates/elements/image.html", + "legacyRegistry" +], function ( + FixedController, + fixedTemplate, + frameTemplate, + telemetryTemplate, + boxTemplate, + lineTemplate, + textTemplate, + imageTemplate, + legacyRegistry +) { + return function() { + return function (openmct) { + openmct.legacyRegistry.register("platform/features/fixed", { + "name": "Fixed position components.", + "description": "Plug in adding Fixed Position object type.", + "extensions": { + "views": [ + { + "key": "fixed-display", + "name": "Fixed Position Display", + "cssClass": "icon-box-with-dashed-lines", + "type": "telemetry.fixed", + "template": fixedTemplate, + "uses": ["composition"], + "editable": true + } + ], + "templates": [ + { + "key": "fixed.telemetry", + "template": telemetryTemplate + }, + { + "key": "fixed.box", + "template": boxTemplate + }, + { + "key": "fixed.line", + "template": lineTemplate + }, + { + "key": "fixed.text", + "template": textTemplate + }, + { + "key": "fixed.image", + "template": imageTemplate + } + ], + "controllers": [ + { + "key": "FixedController", + "implementation": FixedController, + "depends": [ + "$scope", + "$q", + "dialogService", + "openmct", + "$element" + ] + } + ], + "toolbars": [ + { + name: "Fixed Position Toolbar", + key: "fixed.position", + description: "Toolbar for the selected element inside a fixed position display.", + forSelection: function (selection) { + if (!selection) { + return; + } + + return (openmct.editor.isEditing() && + (selection[0] && selection[0].context.elementProxy && + selection[1] && selection[1].context.item.type === 'telemetry.fixed' || + selection[0] && selection[0].context.item.type === 'telemetry.fixed')); + }, + toolbar: function (selection) { + var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"]; + var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"]; + var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"]; + var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"]; + var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"]; + var fixedPageProperties = ["add"]; + + var properties = [], + fixedItem = selection[0] && selection[0].context.item, + elementProxy = selection[0] && selection[0].context.elementProxy, + domainObject = selection[1] && selection[1].context.item, + path; + + if (elementProxy) { + var type = elementProxy.element.type; + path = "configuration['fixed-display'].elements[" + elementProxy.index + "]"; + properties = + type === 'fixed.image' ? imageProperties : + type === 'fixed.text' ? textProperties : + type === 'fixed.box' ? boxProperties : + type === 'fixed.line' ? lineProperties : + type === 'fixed.telemetry' ? telemetryProperties : []; + } else if (fixedItem) { + properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties; + } + + return [ + { + control: "menu", + domainObject: domainObject || selection[0].context.item, + method: function (option) { + selection[0].context.fixedController.add(option.key); + }, + key: "add", + icon: "icon-plus", + label: "Add", + options: [ + { + "name": "Box", + "class": "icon-box", + "key": "fixed.box" + }, + { + "name": "Line", + "class": "icon-line-horz", + "key": "fixed.line" + }, + { + "name": "Text", + "class": "icon-T", + "key": "fixed.text" + }, + { + "name": "Image", + "class": "icon-image", + "key": "fixed.image" + } + ] + }, + { + control: "menu", + domainObject: domainObject, + method: function (option) { + console.log('option', option) + selection[0].context.fixedController.order( + selection[0].context.elementProxy, + option.key + ); + }, + key: "order", + icon: "icon-layers", + title: "Move the selected object above or below other objects", + options: [ + { + "name": "Move to Top", + "class": "icon-arrow-double-up", + "key": "top" + }, + { + "name": "Move Up", + "class": "icon-arrow-up", + "key": "up" + }, + { + "name": "Move Down", + "class": "icon-arrow-down", + "key": "down" + }, + { + "name": "Move to Bottom", + "class": "icon-arrow-double-down", + "key": "bottom" + } + ] + }, + { + control: "color-picker", + domainObject: domainObject, + property: path + ".fill", + icon: "icon-paint-bucket", + title: "Set fill color", + key: 'fill' + }, + { + control: "color-picker", + domainObject: domainObject, + property: path + ".stroke", + icon: "icon-line-horz", + title: "Set border color", + key: 'stroke' + }, + { + control: "button", + domainObject: domainObject, + property: path + ".url", + icon: "icon-image", + title: "Edit image properties", + key: 'url', + dialog: { + control: "input", + type: "text", + name: "Image URL", + class: "l-input-lg", + required: true + } + }, + { + control: "color-picker", + domainObject: domainObject, + property: path + ".color", + icon: "icon-T", + mandatory: true, + title: "Set text color", + key: 'color' + }, + { + control: "select-menu", + domainObject: domainObject, + property: path + ".size", + title: "Set text size", + key: 'size', + options: [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) { + return { "value": size + "px"}; + }) + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".x", + label: "X", + title: "X position", + key: "x", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".y", + label: "Y", + title: "Y position", + key: "y", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".x", + label: "X1", + title: "X1 position", + key: "x1", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".y", + label: "Y1", + title: "Y1 position", + key: "y1", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".x2", + label: "X2", + title: "X2 position", + key: "x2", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".y2", + label: "Y2", + title: "Y2 position", + key: "y2", + class: "l-input-sm", + min: "0" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".height", + label: "H", + title: "Resize object height", + key: "height", + class: "l-input-sm", + min: "1" + }, + { + control: "input", + type: "number", + domainObject: domainObject, + property: path + ".width", + label: "W", + title: "Resize object width", + key: "width", + class: "l-input-sm", + min: "1" + }, + { + control: "toggle-button", + domainObject: domainObject, + property: path + ".useGrid", + key: "useGrid", + options: [ + { + value: true, + icon: 'icon-grid-snap-to', + title: 'Snap to grid' + }, + { + value: false, + icon: 'icon-grid-snap-no', + title: "Do not snap to grid" + } + ] + }, + { + control: "button", + domainObject: domainObject, + property: path + ".text", + icon: "icon-gear", + title: "Edit text properties", + key: "text", + dialog: { + control: "input", + type: "text", + name: "Text", + required: true + } + }, + { + control: "toggle-button", + domainObject: domainObject, + property: path + ".titled", + key: "titled", + options: [ + { + value: true, + icon: 'icon-two-parts-both', + title: 'Show label' + }, + { + value: false, + icon: 'icon-two-parts-one-only', + title: "Hide label" + } + ] + }, + { + control: "button", + domainObject: domainObject, + method: function () { + selection[0].context.fixedController.remove( + selection[0].context.elementProxy + ); + }, + key: "remove", + icon: "icon-trash" + } + ].filter(function (item) { + var filtered; + + properties.forEach(function (property) { + if (item.property && item.key === property || + item.method && item.key === property) { + filtered = item; + } + }); + + return filtered; + }); + } + } + ], + "types": [ + { + "key": "telemetry.fixed", + "name": "Fixed Position Display", + "cssClass": "icon-box-with-dashed-lines", + "description": "Collect and display telemetry elements in " + + "alphanumeric format in a simple canvas workspace. " + + "Elements can be positioned and sized. " + + "Lines, boxes and images can be added as well.", + "priority": 899, + "delegates": [ + "telemetry" + ], + "features": "creation", + "contains": [ + { + "has": "telemetry" + } + ], + "model": { + "layoutGrid": [64, 16], + "composition": [] + }, + "properties": [ + { + "name": "Layout Grid", + "control": "composite", + "items": [ + { + "name": "Horizontal grid (px)", + "control": "textfield", + "cssClass": "l-input-sm l-numeric" + }, + { + "name": "Vertical grid (px)", + "control": "textfield", + "cssClass": "l-input-sm l-numeric" + } + ], + "pattern": "^(\\d*[1-9]\\d*)?$", + "property": "layoutGrid", + "conversion": "number[]" + } + ], + "views": [ + "fixed-display" + ] + } + ] + } + }); + openmct.legacyRegistry.enable("platform/features/fixed"); + } + } +}); \ No newline at end of file diff --git a/platform/features/fixed/src/FixedController.js b/platform/features/fixed/src/FixedController.js index 9a74dcf05d..f18b7ef96e 100644 --- a/platform/features/fixed/src/FixedController.js +++ b/platform/features/fixed/src/FixedController.js @@ -225,6 +225,8 @@ define( this.openmct.time.on("bounds", updateDisplayBounds); this.openmct.selection.on('change', this.setSelection.bind(this)); + this.openmct.editor.on('isEditing', this.handleEditing.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)); @@ -421,6 +423,7 @@ define( this.openmct.selection.off("change", this.setSelection); this.composition.off('add', this.onCompositionAdd, this); this.composition.off('remove', this.onCompositionRemove, this); + this.openmct.editor.off('isEditing', this.handleEditing, this); }; /** @@ -706,6 +709,12 @@ define( this.openmct.objects.mutate(this.newDomainObject, path, value); }; + FixedController.prototype.handleEditing = function (isEditing) { + // Listen for edit mode changes and update selection if necessary. + // Mainly to ensure fixedController is on the selection context when editing. + this.setSelection(this.openmct.selection.get()); + } + return FixedController; } ); diff --git a/platform/features/fixed/src/elements/ElementFactory.js b/platform/features/fixed/src/elements/ElementFactory.js index 576375f819..d9c6475e3b 100644 --- a/platform/features/fixed/src/elements/ElementFactory.js +++ b/platform/features/fixed/src/elements/ElementFactory.js @@ -42,7 +42,8 @@ define( }, "fixed.text": { fill: "transparent", - stroke: "transparent" + stroke: "transparent", + size: "13px" } }, DIALOGS = { diff --git a/platform/features/fixed/src/elements/TextProxy.js b/platform/features/fixed/src/elements/TextProxy.js index 3128dc6b7e..1aac034cee 100644 --- a/platform/features/fixed/src/elements/TextProxy.js +++ b/platform/features/fixed/src/elements/TextProxy.js @@ -67,10 +67,6 @@ define( */ proxy.size = new AccessorMutator(element, 'size'); - if (proxy.size() === undefined) { - proxy.size("13px"); - } - return proxy; } diff --git a/platform/features/fixed/templates/fixed.html b/platform/features/fixed/templates/fixed.html index e984f2d6cd..bdeca501d4 100644 --- a/platform/features/fixed/templates/fixed.html +++ b/platform/features/fixed/templates/fixed.html @@ -23,18 +23,18 @@ ng-controller="FixedController as controller"> -
-
+
-
@@ -44,15 +44,15 @@
- -
+
@import "~styles/sass-base"; - .l-layout, - .c-grid, - .c-grid__x, - .c-grid__y { - @include abs(); - } - .l-layout { + @include abs(); display: flex; flex-direction: column; @@ -78,34 +72,15 @@ } } - .c-grid { - pointer-events: none; - - &__x { @include bgTicks($colorGridLines, 'x'); } - &__y { @include bgTicks($colorGridLines, 'y'); } - } - - .is-editing { - .l-shell__main-container > .l-layout { - // Target the top-most layout container and color its background - background: rgba($editColor, 0.1); - } - - [s-selected], - [s-selected-parent] { - .l-layout { - // Show the layout grid for the top-most child of the current selection, - // and hide the grid for deeper nested levels. - [class*="__grid-holder"] { - display: block; - } - - .l-layout [class*="__grid-holder"] { - display: none; - } + .l-shell__main-container { + > .l-layout { + [s-selected] { + border: $browseBorderSelected; } } } + + // Styles moved to _global.scss; @@ -237,22 +212,17 @@ return; } - let domainObject = selection[0].context.item; - if (domainObject && domainObject === this.selectedObject) { - return; - } - - this.selectedObject = domainObject; this.removeListeners(); + let domainObject = selection[0].context.item; - if (selection[1]) { - this.attachSelectionListeners(); + if (selection[1] && domainObject) { + this.attachSelectionListeners(domainObject.identifier); } this.updateDrilledInState(); }, - attachSelectionListeners() { - let id = this.openmct.objects.makeKeyString(this.selectedObject.identifier); + attachSelectionListeners(identifier) { + let id = this.openmct.objects.makeKeyString(identifier); let path = "configuration.layout.panels[" + id + "]"; this.listeners.push(this.openmct.objects.observe(this.newDomainObject, path + ".hasFrame", function (newValue) { this.frameItems.forEach(function (item) { diff --git a/src/plugins/displayLayout/LayoutFrame.vue b/src/plugins/displayLayout/LayoutFrame.vue index c4368838fc..9494cde4e8 100644 --- a/src/plugins/displayLayout/LayoutFrame.vue +++ b/src/plugins/displayLayout/LayoutFrame.vue @@ -132,110 +132,8 @@ padding: $interiorMargin; } - /*************************** SELECTION */ - &.is-selectable { - &:hover { - box-shadow: $browseShdwSelectableHov; - } - } - - &[s-selected], // LEGACY - &.is-selected { - border: $browseBorderSelected; - } + // Styles moved to _global.scss; } - - /*************************** EDITING */ - .is-editing { - .c-frame { - &:not(.is-drilled-in).is-selectable { - border: $editBorderSelectable; - - &:hover { - border: $editBorderSelectableHov; - } - - &[s-selected], - &.is-selected { - border: $editBorderSelected; - - > .c-frame-edit { - display: block; // Show the editing rect and handles - } - } - } - - &.is-drilled-in { - border: $editBorderDrilledIn; - } - - .u-links { - // Applied in markup to objects that provide links. Disable while editing. - pointer-events: none; - } - } - } - - .c-frame-edit { - // The editing rect and handles - $z: 10; - - @include abs(); - box-shadow: rgba($editColor, 0.5) 0 0 10px; - display: none; - - &__move { - @include abs(); - cursor: move; - z-index: $z; - } - - &__handle { - $d: 8px; - $o: floor($d * -0.5); - background: rgba($editColor, 0.3); - border: 1px solid $editColor; - position: absolute; - width: $d; height: $d; - top: auto; right: auto; bottom: auto; left: auto; - z-index: $z + 1; - - &:before { - // Extended hit area - $m: -5px; - content: ''; - display: block; - position: absolute; - top: $m; right: $m; bottom: $m; left: $m; - z-index: -1; - } - - &:hover { - background: $editColor; - } - - &.--nw { - cursor: nw-resize; - left: $o; top: $o; - } - - &.--ne { - cursor: ne-resize; - right: $o; top: $o; - } - - &.--se { - cursor: se-resize; - right: $o; bottom: $o; - } - - &.--sw { - cursor: sw-resize; - left: $o; bottom: $o; - } - } - } - diff --git a/src/plugins/displayLayout/plugin.js b/src/plugins/displayLayout/plugin.js index 6e7fb53324..a9bdbe19de 100644 --- a/src/plugins/displayLayout/plugin.js +++ b/src/plugins/displayLayout/plugin.js @@ -72,6 +72,7 @@ export default function () { // and in edit mode. return (selection && selection[1] && + selection[1].context.item && selection[1].context.item.type === 'layout' && openmct.editor.isEditing()); }, diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 17e2c17752..7efb3da0c2 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -36,7 +36,8 @@ define([ './staticRootPlugin/plugin', './notebook/plugin', './displayLayout/plugin', - './folderView/plugin' + './folderView/plugin', + '../../platform/features/fixed/plugin' ], function ( _, UTCTimeSystem, @@ -53,7 +54,8 @@ define([ StaticRootPlugin, Notebook, DisplayLayoutPlugin, - FolderView + FolderView, + FixedView ) { var bundleMap = { LocalStorage: 'platform/persistence/local', @@ -165,6 +167,7 @@ define([ plugins.Notebook = Notebook; plugins.DisplayLayout = DisplayLayoutPlugin.default; plugins.FolderView = FolderView; + plugins.FixedView = FixedView; return plugins; }); diff --git a/src/plugins/telemetryTable/TableConfigurationViewProvider.js b/src/plugins/telemetryTable/TableConfigurationViewProvider.js index 1460be1262..1ece6d5e6e 100644 --- a/src/plugins/telemetryTable/TableConfigurationViewProvider.js +++ b/src/plugins/telemetryTable/TableConfigurationViewProvider.js @@ -41,7 +41,7 @@ define([ return false; } let object = selection[0].context.item; - return object.type === 'table'; + return object && object.type === 'table'; }, view: function (selection) { let component; diff --git a/src/styles-new/_global.scss b/src/styles-new/_global.scss index e939fb74a8..2d40142acc 100644 --- a/src/styles-new/_global.scss +++ b/src/styles-new/_global.scss @@ -229,6 +229,157 @@ body.desktop .has-local-controls { //} //} +/******************************************************** SELECTION AND EDITING */ +// Provides supporting styles for Display Layouts and augmented legacy Fixed Position view + +.c-grid, +.c-grid__x, +.c-grid__y { + @include abs(); +} + +.c-grid { + pointer-events: none; + + &__x { @include bgTicks($colorGridLines, 'x'); } + &__y { @include bgTicks($colorGridLines, 'y'); } +} + +/*************************** SELECTION */ +.is-selectable { + &:hover { + box-shadow: $browseShdwSelectableHov; + } +} + +/**************************** EDITING */ +.is-editing { + *:not(.is-drilled-in).is-selectable { + border: $editBorderSelectable; + + &:hover { + border: $editBorderSelectableHov; + } + + &[s-selected], + &.is-selected { + border: $editBorderSelected; + + > .c-frame-edit { + display: block; // Show the editing rect and handles + } + } + } + + *.is-drilled-in { + border: $editBorderDrilledIn; + } + + .u-links { + // Applied in markup to objects that provide links. Disable while editing. + pointer-events: none; + } + + .c-frame-edit { + // In Layouts, this is the editing rect and handles + // In Fixed Position, this is a wrapper element + $z: 10; + + @include abs(); + display: none; + + &__move { + @include abs(); + box-shadow: rgba($editColor, 0.5) 0 0 10px; + cursor: move; + z-index: $z; + } + + &__handle { + $d: 8px; + $o: floor($d * -0.5); + background: rgba($editColor, 0.3); + border: 1px solid $editColor; + position: absolute; + width: $d; height: $d; + top: auto; right: auto; bottom: auto; left: auto; + z-index: $z + 1; + + &:before { + // Extended hit area + $m: -5px; + content: ''; + display: block; + position: absolute; + top: $m; right: $m; bottom: $m; left: $m; + z-index: -1; + } + + &:hover { + background: $editColor; + } + + &--nwse { + cursor: nwse-resize; + } + + &--nw { + cursor: nw-resize; + left: $o; top: $o; + } + + &--ne { + cursor: ne-resize; + right: $o; top: $o; + } + + &--se { + cursor: se-resize; + right: $o; bottom: $o; + } + + &--sw { + cursor: sw-resize; + left: $o; bottom: $o; + } + } + } + + .l-shell__main-container > .l-layout, + .l-shell__main-container > .c-object-view .l-fixed-position { + // Target the top-most layout container and color its background + background: rgba($editColor, 0.1); + } + + // Layouts + [s-selected], + [s-selected-parent] { + .l-layout { + // Show the layout grid for the top-most child of the current selection, + // and hide the grid for deeper nested levels. + [class*="__grid-holder"] { + display: block; + } + + .l-layout [class*="__grid-holder"] { + display: none; + } + } + } + + // Fixed position + .l-fixed-position { + &__grid-holder { + display: block; + } + + .c-frame-edit { + display: block; + } + } +} + + /************************** LEGACY */ mct-container { diff --git a/src/ui/components/inspector/Properties.vue b/src/ui/components/inspector/Properties.vue index 6a2a493809..47af06b73d 100644 --- a/src/ui/components/inspector/Properties.vue +++ b/src/ui/components/inspector/Properties.vue @@ -4,19 +4,19 @@
  • Title
    -
    {{ domainObject.name }}
    +
    {{ item.name }}
  • Type
    {{ typeName }}
  • -
  • +
  • Created
    -
    {{ domainObject.created }}
    +
    {{ item.created }}
  • -
  • +
  • Modified
    -
    {{ domainObject.modified }}
    +
    {{ item.modified }}
  • { return object[field]; - }, this.domainObject) + }, this.item) }; }); } diff --git a/src/ui/components/toolbar/Toolbar.vue b/src/ui/components/toolbar/Toolbar.vue index ead18d8c06..34cc831c1c 100644 --- a/src/ui/components/toolbar/Toolbar.vue +++ b/src/ui/components/toolbar/Toolbar.vue @@ -1,8 +1,9 @@ @@ -36,18 +37,14 @@ }, methods: { handleSelection(selection) { + this.removeListeners(); + this.domainObjectsById = {}; + if (!selection[0]) { this.structure = []; - this.selectedObject = undefined; - this.removeListeners(); return; } - let domainObject = selection[0].context.item; - - this.selectedObject = domainObject; - this.removeListeners(); - let structure = this.openmct.toolbars.get(selection) || []; this.structure = structure.map(function (item) { let toolbarItem = {...item}; @@ -58,19 +55,56 @@ }.bind(this)); }, registerListener(domainObject) { + let id = this.openmct.objects.makeKeyString(domainObject.identifier); + + if (!this.domainObjectsById[id]) { + this.domainObjectsById[id] = { + domainObject: domainObject + } + this.observeObject(domainObject, id); + } + }, + observeObject(domainObject, id) { let unobserveObject = this.openmct.objects.observe(domainObject, '*', function(newObject) { - let newObjectId = this.openmct.objects.makeKeyString(newObject.identifier); - this.structure = this.structure.map((item) => { - let toolbarItem = {...item}; - let domainObjectId = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier); - if (domainObjectId === newObjectId) { - toolbarItem.domainObject = JSON.parse(JSON.stringify(newObject)); - } - return toolbarItem; - }); + this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject)); + this.scheduleToolbarUpdate(); }.bind(this)); this.unObserveObjects.push(unobserveObject); }, + scheduleToolbarUpdate() { + if (this.toolbarUpdateScheduled) { + return; + } + + this.toolbarUpdateScheduled = true; + setTimeout(this.updateToolbarAfterMutation.bind(this)); + }, + updateToolbarAfterMutation() { + this.structure = this.structure.map((item) => { + let toolbarItem = {...item}; + let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier); + let newObject = this.domainObjectsById[id].newObject; + + if (newObject) { + toolbarItem.domainObject = newObject; + let newValue = _.get(newObject, item.property); + + if (toolbarItem.value !== newValue) { + toolbarItem.value = newValue; + } + } + + return toolbarItem; + }); + + Object.values(this.domainObjectsById).forEach(function (tracker) { + if (tracker.newObject) { + tracker.domainObject = tracker.newObject; + delete tracker.newObject; + } + }); + this.toolbarUpdateScheduled = false; + }, removeListeners() { if (this.unObserveObjects) { this.unObserveObjects.forEach((unObserveObject) => { @@ -80,15 +114,26 @@ this.unObserveObjects = []; }, updateObjectValue(value, item) { - let changedId = this.openmct.objects.makeKeyString(item.domainObject.identifier); + let changedItemId = this.openmct.objects.makeKeyString(item.domainObject.identifier); this.structure = this.structure.map((s) => { let toolbarItem = {...s}; - if (changedId === this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier)) { + let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier); + + if (changedItemId === id && item.property === s.property) { toolbarItem.value = value; } + return toolbarItem; }); this.openmct.objects.mutate(item.domainObject, item.property, value); + }, + triggerMethod(item, event) { + if (item.method) { + item.method({...event}); + } + }, + handleEditing(isEditing) { + this.handleSelection(this.openmct.selection.get()); } }, mounted() { @@ -97,12 +142,11 @@ // Toolbars may change when edit mode is enabled/disabled, so listen // for edit mode changes and update toolbars if necessary. - this.openmct.editor.on('isEditing', (isEditing) => { - this.handleSelection(this.openmct.selection.get()); - }); + this.openmct.editor.on('isEditing', this.handleEditing); }, detroyed() { this.openmct.selection.off('change', this.handleSelection); + this.openmct.editor.off('isEditing', this.handleEditing); this.removeListeners(); } } diff --git a/src/ui/components/toolbar/components/toolbar-button.vue b/src/ui/components/toolbar/components/toolbar-button.vue index 9e21f3840d..cb6f4a6d65 100644 --- a/src/ui/components/toolbar/components/toolbar-button.vue +++ b/src/ui/components/toolbar/components/toolbar-button.vue @@ -1,6 +1,7 @@