Merge branch 'master' into adding-units

Merg'n main branch
This commit is contained in:
Jamie Vigliotta 2020-07-14 10:25:39 -07:00
commit 3735a85c69
41 changed files with 620 additions and 235 deletions

3
.gitignore vendored
View File

@ -37,4 +37,7 @@ protractor/logs
# npm-debug log
npm-debug.log
# karma reports
report.*.json
package-lock.json

View File

@ -59,7 +59,7 @@
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"node-sass": "^4.9.2",
"painterro": "^0.2.65",
"painterro": "^1.0.35",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",

View File

@ -19,7 +19,13 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="c-object-label">
<div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
<div class="c-object-label"
ng-class="{ 'is-missing': model.status === 'missing' }"
>
<div class="c-object-label__type-icon {{type.getCssClass()}}"
ng-class="{ 'l-icon-link':location.isLink() }"
>
<span class="is-missing__indicator" title="This item is missing"></span>
</div>
<div class='c-object-label__name'>{{model.name}}</div>
</div>

View File

@ -31,10 +31,16 @@
<div
v-if="domainObject"
class="c-telemetry-view"
:class="styleClass"
:class="{
styleClass,
'is-missing': domainObject.status === 'missing'
}"
:style="styleObject"
@contextmenu.prevent="showContextMenu"
>
<div class="is-missing__indicator"
title="This item is missing"
></div>
<div
v-if="showLabel"
class="c-telemetry-view__label"

View File

@ -26,4 +26,14 @@
@include abs();
border: 1px solid transparent;
}
&.is-missing {
@include isMissing($absPos: true);
border: $borderMissing;
.is-missing__indicator {
top: 0;
left: 0;
}
}
}

View File

@ -1,13 +1,18 @@
<template>
<a
class="l-grid-view__item c-grid-item"
:class="{ 'is-alias': item.isAlias === true }"
:class="{
'is-alias': item.isAlias === true,
'is-missing': item.model.status === 'missing',
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
}"
:href="objectLink"
>
<div
class="c-grid-item__type-icon"
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
></div>
>
</div>
<div class="c-grid-item__details">
<!-- Name and metadata -->
<div
@ -22,6 +27,9 @@
</div>
</div>
<div class="c-grid-item__controls">
<div class="is-missing__indicator"
title="This item is missing"
></div>
<div
class="icon-people"
title="Shared"

View File

@ -7,13 +7,19 @@
<td class="c-list-item__name">
<a
ref="objectLink"
class="c-object-label"
:class="{ 'is-missing': item.model.status === 'missing' }"
:href="objectLink"
>
<div
class="c-list-item__type-icon"
class="c-object-label__type-icon c-list-item__type-icon"
:class="item.type.cssClass"
></div>
<div class="c-list-item__name-value">{{ item.model.name }}</div>
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-object-label__name c-list-item__name">{{ item.model.name }}</div>
</a>
</td>
<td class="c-list-item__type">

View File

@ -38,7 +38,15 @@
// Object is an alias to an original.
[class*='__type-icon'] {
@include isAlias();
color: $colorIconAliasForKeyFilter;
}
}
&.is-missing {
@include isMissing();
[class*='__type-icon'],
[class*='__details'] {
opacity: $opacityMissing;
}
}
@ -85,15 +93,14 @@
body.desktop & {
$transOutMs: 300ms;
flex-flow: column nowrap;
transition: background $transOutMs ease-in-out;
transition: $transOutMs ease-in-out;
&:hover {
background: $colorItemBgHov;
filter: $filterItemHoverFg;
transition: $transIn;
.c-grid-item__type-icon {
filter: $colorKeyFilterHov;
transform: scale(1);
transform: scale(1.1);
transition: $transInBounce;
}
}
@ -103,7 +110,7 @@
}
&__controls {
align-items: start;
align-items: baseline;
flex: 0 0 auto;
order: 1;
.c-info-button,
@ -115,7 +122,6 @@
font-size: floor($gridItemDesk / 3);
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
order: 2;
transform: scale(0.9);
transform-origin: center;
transition: all $transOutMs ease-in-out;
}

View File

@ -1,37 +1,17 @@
/******************************* LIST ITEM */
.c-list-item {
&__name a {
display: flex;
> * + * { margin-left: $interiorMarginSm; }
}
&__type-icon {
// Have to do it this way instead of using icon-* class, due to need to apply alias to the icon
color: $colorKey;
display: inline-block;
width: 1em;
margin-right:$interiorMarginSm;
color: $colorItemTreeIcon;
}
&__name-value {
&__name {
@include ellipsize();
}
&.is-alias {
// Object is an alias to an original.
[class*='__type-icon'] {
&:after {
color: $colorIconAlias;
content: $glyph-icon-link;
font-family: symbolsfont;
display: block;
position: absolute;
text-shadow: rgba(black, 0.5) 0 1px 2px;
top: auto; left: -1px; bottom: 1px; right: auto;
transform-origin: bottom left;
transform: scale(0.65);
}
@include isAlias();
}
}
}

View File

@ -13,7 +13,8 @@
cursor: pointer;
&:hover {
background: $colorListItemBgHov;
background: $colorItemTreeHoverBg;
filter: $filterHov;
transition: $transIn;
}
}

View File

@ -28,17 +28,22 @@
ng-click="legend.set('expanded', !legend.get('expanded'));">
</div>
<div class="c-plot-legend__wrapper">
<div class="c-plot-legend__wrapper"
ng-class="{ 'is-cursor-locked': !!lockHighlightPoint }">
<!-- COLLAPSED PLOT LEGEND -->
<div class="plot-wrapper-collapsed-legend"
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
ng-class="{'is-cursor-locked': !!lockHighlightPoint }">
<div class="c-state-indicator__alert-cursor-lock icon-cursor-lock" title="Cursor is point locked. Click anywhere in the plot to unlock."></div>
<div class="plot-legend-item"
ng-repeat="series in series track by $index">
ng-class="{'is-missing': series.domainObject.status === 'missing'}"
ng-repeat="series in series track by $index"
>
<div class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
<span class="plot-series-color-swatch "
ng-style="{ 'background-color': series.get('color').asHexString() }">
</span>
<span class="is-missing__indicator" title="This item is missing"></span>
<span class="plot-series-name">{{ series.get('name') }}</span>
</div>
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
@ -55,7 +60,10 @@
</div>
<!-- EXPANDED PLOT LEGEND -->
<div class="plot-wrapper-expanded-legend">
<div class="plot-wrapper-expanded-legend"
ng-class="{'is-cursor-locked': !!lockHighlightPoint }"
>
<div class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock" title="Click anywhere in the plot to unlock."> Cursor locked to point</div>
<table>
<thead>
<tr>
@ -76,12 +84,15 @@
</th>
</tr>
</thead>
<tr ng-repeat="series in series" class="plot-legend-item">
<td class="plot-series-swatch-and-name"
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
<tr ng-repeat="series in series"
class="plot-legend-item"
ng-class="{'is-missing': series.domainObject.status === 'missing'}"
>
<td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
ng-style="{ 'background-color': series.get('color').asHexString() }">
</span>
<span class="is-missing__indicator" title="This item is missing"></span>
<span class="plot-series-name">{{ series.get('name') }}</span>
</td>
@ -134,7 +145,7 @@
{{option.name}}
</option>
</select>
<mct-ticks axis="yAxis">
<div ng-repeat="tick in ticks track by tick.value"

View File

@ -44,7 +44,7 @@
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The line rendering style for this series.">Line Style</div>
title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value">{{ {
'none': 'None',
'linear': 'Linear interpolation',
@ -56,7 +56,7 @@
<div class="grid-cell label"
title="Whether markers are displayed, and their size.">Markers</div>
<div class="grid-cell value">
{{series.get('markers') ? "Enabled: " + series.get('markerSize') + "px" : "Disabled"}}
{{ series.markerOptionsDisplayText() }}
</div>
</li>
<li class="grid-row">

View File

@ -52,7 +52,7 @@
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The line rendering style for this series.">Line Style</div>
title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value">
<select ng-model="form.interpolate">
<option value="none">None</option>
@ -64,12 +64,27 @@
<li class="grid-row">
<div class="grid-cell label"
title="Whether markers are displayed.">Markers</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.markers"/></div>
<div class="grid-cell value">
<input type="checkbox" ng-model="form.markers"/>
<select
ng-show="form.markers"
ng-model="form.markerShape">
<option
ng-repeat="option in markerShapeOptions"
value="{{ option.value }}"
ng-selected="option.value == form.markerShape"
>
{{ option.name }}
</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display markers visually denoting points in alarm.">Alarm Markers</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.alarmMarkers"/></div>
<div class="grid-cell value">
<input type="checkbox" ng-model="form.alarmMarkers"/>
</div>
</li>
<li class="grid-row" ng-show="form.markers || form.alarmMarkers">
<div class="grid-cell label"

View File

@ -371,7 +371,8 @@ function (
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count,
chartElement.series.get('markerSize')
chartElement.series.get('markerSize'),
chartElement.series.get('markerShape')
);
};
@ -395,9 +396,10 @@ function (
this.offset.yVal(highlight.point, highlight.series)
]),
color = highlight.series.get('color').asRGBAArray(),
pointCount = 1;
pointCount = 1,
shape = highlight.series.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE);
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
};
MCTChartController.prototype.drawRectangles = function () {

View File

@ -25,12 +25,14 @@ define([
'lodash',
'../configuration/Model',
'../lib/extend',
'EventEmitter'
'EventEmitter',
'../draw/MarkerShapes'
], function (
_,
Model,
extend,
EventEmitter
EventEmitter,
MARKER_SHAPES
) {
/**
@ -56,6 +58,7 @@ define([
* `linear` (points are connected via straight lines), or
* `stepAfter` (points are connected by steps).
* `markers`: boolean, whether or not this series should render with markers.
* `markerShape`: string, shape of markers.
* `markerSize`: number, size in pixels of markers for this series.
* `alarmMarkers`: whether or not to display alarm markers for this series.
* `stats`: An object that tracks the min and max y values observed in this
@ -101,6 +104,7 @@ define([
xKey: options.collection.plot.xAxis.get('key'),
yKey: range.key,
markers: true,
markerShape: 'point',
markerSize: 2.0,
alarmMarkers: true
};
@ -410,6 +414,18 @@ define([
} else {
this.filters = deepCopiedFilters;
}
},
markerOptionsDisplayText: function () {
const showMarkers = this.get('markers');
if (!showMarkers) {
return "Disabled";
}
const markerShapeKey = this.get('markerShape');
const markerShape = MARKER_SHAPES[markerShapeKey].label;
const markerSize = this.get('markerSize');
return `${markerShape}: ${markerSize}px`;
}
});

View File

@ -23,10 +23,12 @@
define([
'EventEmitter',
'../lib/eventHelpers'
'../lib/eventHelpers',
'./MarkerShapes'
], function (
EventEmitter,
eventHelpers
eventHelpers,
MARKER_SHAPES
) {
/**
@ -121,18 +123,17 @@ define([
buf,
color,
points,
pointSize
pointSize,
shape
) {
var i = 0,
offset = pointSize / 2;
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
this.setColor(color);
for (; i < points; i++) {
this.c2d.fillRect(
this.x(buf[i * 2]) - offset,
this.y(buf[i * 2 + 1]) - offset,
pointSize,
for (let i = 0; i < points; i++) {
drawC2DShape(
this.x(buf[i * 2]),
this.y(buf[i * 2 + 1]),
pointSize
);
}

View File

@ -23,30 +23,64 @@
define([
'EventEmitter',
'../lib/eventHelpers'
'../lib/eventHelpers',
'./MarkerShapes'
], function (
EventEmitter,
eventHelpers
eventHelpers,
MARKER_SHAPES
) {
// WebGL shader sources (for drawing plain colors)
var FRAGMENT_SHADER = [
"precision mediump float;",
"uniform vec4 uColor;",
"void main(void) {",
"gl_FragColor = uColor;",
"}"
].join('\n'),
VERTEX_SHADER = [
"attribute vec2 aVertexPosition;",
"uniform vec2 uDimensions;",
"uniform vec2 uOrigin;",
"uniform float uPointSize;",
"void main(void) {",
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
"gl_PointSize = uPointSize;",
"}"
].join('\n');
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 uColor;
uniform int uMarkerShape;
void main(void) {
gl_FragColor = uColor;
if (uMarkerShape > 1) {
vec2 clipSpacePointCoord = 2.0 * gl_PointCoord - 1.0;
if (uMarkerShape == 2) { // circle
float distance = length(clipSpacePointCoord);
if (distance > 1.0) {
discard;
}
} else if (uMarkerShape == 3) { // diamond
float distance = abs(clipSpacePointCoord.x) + abs(clipSpacePointCoord.y);
if (distance > 1.0) {
discard;
}
} else if (uMarkerShape == 4) { // triangle
float x = clipSpacePointCoord.x;
float y = clipSpacePointCoord.y;
float distance = 2.0 * x - 1.0;
float distance2 = -2.0 * x - 1.0;
if (distance > y || distance2 > y) {
discard;
}
}
}
}
`;
const VERTEX_SHADER = `
attribute vec2 aVertexPosition;
uniform vec2 uDimensions;
uniform vec2 uOrigin;
uniform float uPointSize;
void main(void) {
gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);
gl_PointSize = uPointSize;
}
`;
/**
* Create a draw api utilizing WebGL.
@ -90,6 +124,7 @@ define([
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
this.gl.compileShader(this.vertexShader);
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
this.gl.compileShader(this.fragmentShader);
@ -105,6 +140,7 @@ define([
// shader programs (to pass values into shaders at draw-time)
this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
this.uColor = this.gl.getUniformLocation(this.program, "uColor");
this.uMarkerShape = this.gl.getUniformLocation(this.program, "uMarkerShape");
this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
@ -114,9 +150,6 @@ define([
// Create a buffer to holds points which will be drawn
this.buffer = this.gl.createBuffer();
// Use a line width of 2.0 for legibility
this.gl.lineWidth(2.0);
// Enable blending, for smoothness
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
@ -138,14 +171,18 @@ define([
((v - this.origin[1]) / this.dimensions[1]) * this.height;
};
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) {
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
if (this.isContextLost) {
return;
}
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
this.gl.uniform4fv(this.uColor, color);
this.gl.uniform1i(this.uMarkerShape, shapeCode)
this.gl.drawArrays(drawType, 0, points);
};
@ -210,12 +247,12 @@ define([
* Draw the buffer as points.
*
*/
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
if (this.isContextLost) {
return;
}
this.gl.uniform1f(this.uPointSize, pointSize);
this.doDraw(this.gl.POINTS, buf, color, points);
this.doDraw(this.gl.POINTS, buf, color, points, shape);
};
/**

View File

@ -0,0 +1,90 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, 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 () {
/**
* @label string (required) display name of shape
* @drawWebGL integer (unique, required) index provided to WebGL Fragment Shader
* @drawC2D function (required) canvas2d draw function
*/
const MARKER_SHAPES = {
point: {
label: 'Point',
drawWebGL: 1,
drawC2D: function (x, y, size) {
const offset = size / 2;
this.c2d.fillRect(x - offset, y - offset, size, size);
}
},
circle: {
label: 'Circle',
drawWebGL: 2,
drawC2D: function (x, y, size) {
const radius = size / 2;
this.c2d.beginPath();
this.c2d.arc(x, y, radius, 0, 2 * Math.PI, false);
this.c2d.closePath();
this.c2d.fill();
}
},
diamond: {
label: 'Diamond',
drawWebGL: 3,
drawC2D: function (x, y, size) {
const offset = size / 2;
const top = [x, y + offset];
const right = [x + offset, y];
const bottom = [x, y - offset];
const left = [x - offset, y];
this.c2d.beginPath();
this.c2d.moveTo(...top);
this.c2d.lineTo(...right);
this.c2d.lineTo(...bottom);
this.c2d.lineTo(...left);
this.c2d.closePath();
this.c2d.fill();
}
},
triangle: {
label: 'Triangle',
drawWebGL: 4,
drawC2D: function (x, y, size) {
const offset = size / 2;
const v1 = [x, y - offset];
const v2 = [x - offset, y + offset];
const v3 = [x + offset, y + offset];
this.c2d.beginPath();
this.c2d.moveTo(...v1);
this.c2d.lineTo(...v2);
this.c2d.lineTo(...v3);
this.c2d.closePath();
this.c2d.fill();
}
}
};
return MARKER_SHAPES;
});

View File

@ -22,9 +22,11 @@
define([
'./PlotModelFormController',
'../draw/MarkerShapes',
'lodash'
], function (
PlotModelFormController,
MARKER_SHAPES,
_
) {
@ -93,6 +95,13 @@ define([
value: o.key
};
});
this.$scope.markerShapeOptions = Object.entries(MARKER_SHAPES)
.map(([key, obj]) => {
return {
name: obj.label,
value: key
};
});
},
fields: [
@ -108,6 +117,10 @@ define([
modelProp: 'markers',
objectPath: dynamicPathForKey('markers')
},
{
modelProp: 'markerShape',
objectPath: dynamicPathForKey('markerShape')
},
{
modelProp: 'markerSize',
coerce: Number,

View File

@ -71,8 +71,6 @@ define([
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
};
MCTPlotController.prototype.initialize = function () {
@ -83,11 +81,6 @@ define([
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
this.listenTo(this.$window, 'keydown', this.toggleInteractionMode, this);
this.listenTo(this.$window, 'keyup', this.resetInteractionMode, this);
this.$scope.rectangles = [];
this.$scope.tickWidth = 0;
@ -243,12 +236,16 @@ define([
};
MCTPlotController.prototype.onMouseDown = function ($event) {
// do not monitor drag events on browser context click
if (event.ctrlKey) {
return;
}
this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
if (this.allowPan) {
if (event.altKey) {
return this.startPan($event);
}
if (this.allowMarquee) {
} else {
return this.startMarquee($event);
}
};
@ -261,11 +258,11 @@ define([
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
}
if (this.allowPan) {
if (this.pan) {
return this.endPan($event);
}
if (this.allowMarquee) {
if (this.marquee) {
return this.endMarquee($event);
}
};
@ -289,6 +286,9 @@ define([
};
MCTPlotController.prototype.startMarquee = function ($event) {
this.$canvas.removeClass('plot-drag');
this.$canvas.addClass('plot-marquee');
this.trackMousePosition($event);
if (this.positionOverPlot) {
this.freeze();
@ -444,6 +444,9 @@ define([
};
MCTPlotController.prototype.startPan = function ($event) {
this.$canvas.addClass('plot-drag');
this.$canvas.removeClass('plot-marquee');
this.trackMousePosition($event);
this.freeze();
this.pan = {
@ -486,32 +489,6 @@ define([
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.watchForMarquee = function () {
this.$canvas.removeClass('plot-drag');
this.$canvas.addClass('plot-marquee');
this.allowPan = false;
this.allowMarquee = true;
};
MCTPlotController.prototype.watchForPan = function () {
this.$canvas.addClass('plot-drag');
this.$canvas.removeClass('plot-marquee');
this.allowPan = true;
this.allowMarquee = false;
};
MCTPlotController.prototype.toggleInteractionMode = function (event) {
if (event.keyCode === 18) { // control key.
this.watchForPan();
}
};
MCTPlotController.prototype.resetInteractionMode = function (event) {
if (event.keyCode === 18) {
this.watchForMarquee();
}
};
MCTPlotController.prototype.freeze = function () {
this.config.yAxis.set('frozen', true);
this.config.xAxis.set('frozen', true);

View File

@ -1,7 +0,0 @@
# Espresso Theme
A light colored theme for the Open MCT user interface.
## Installation
```js
openmct.install(openmct.plugins.Snow());
```

View File

@ -3,7 +3,7 @@
<div
class="c-tabs-view__tabs-holder c-tabs"
:class="{
'is-dragging': isDragging,
'is-dragging': isDragging && allowEditing,
'is-mouse-over': allowDrop
}"
>
@ -22,14 +22,21 @@
<button
v-for="(tab,index) in tabsList"
:key="index"
class="c-tabs-view__tab c-tab"
:class="[
{'is-current': isCurrent(tab)},
tab.type.definition.cssClass
]"
class="c-tabs-view__tab c-tab c-object-label"
:class="{
'is-current': isCurrent(tab),
'is-missing': tab.domainObject.status === 'missing'
}"
@click="showTab(tab, index)"
>
<span class="c-button__label">{{ tab.domainObject.name }}</span>
<div class="c-object-label__type-icon"
:class="tab.type.definition.cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</button>
</div>
<div
@ -38,15 +45,6 @@
class="c-tabs-view__object-holder"
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
>
<div
v-if="currentTab"
class="c-tabs-view__object-name c-object-label l-browse-bar__object-name--w"
:class="currentTab.type.definition.cssClass"
>
<div class="l-browse-bar__object-name c-object-label__name">
{{ currentTab.domainObject.name }}
</div>
</div>
<object-view
v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
class="c-tabs-view__object"
@ -58,6 +56,12 @@
<script>
import ObjectView from '../../../ui/components/ObjectView.vue';
import {
getSearchParam,
setSearchParam,
deleteSearchParam
} from 'utils/openmctLocation';
var unknownObjectType = {
definition: {
@ -71,26 +75,45 @@ export default {
components: {
ObjectView
},
props: {
isEditing: {
type: Boolean,
required: true
}
},
data: function () {
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
return {
internalDomainObject: this.domainObject,
currentTab: {},
currentTabIndex: undefined,
tabsList: [],
setCurrentTab: true,
isDragging: false,
allowDrop: false
allowDrop: false,
searchTabKey: `tabs.pos.${keyString}`
};
},
computed: {
allowEditing() {
return !this.internalDomainObject.locked && this.isEditing;
}
},
mounted() {
if (this.composition) {
this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.onReorder);
this.composition.load().then(() => {
let currentTabIndex = this.domainObject.currentTabIndex;
let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
if (currentTabIndex !== undefined && this.tabsList.length > currentTabIndex) {
this.currentTab = this.tabsList[currentTabIndex];
if (currentTabIndexFromURL !== null) {
this.setCurrentTabByIndex(currentTabIndexFromURL);
} else if (currentTabIndexFromDomainObject !== undefined) {
this.setCurrentTabByIndex(currentTabIndexFromDomainObject);
this.storeCurrentTabIndexInURL(currentTabIndexFromDomainObject);
}
});
}
@ -100,20 +123,29 @@ export default {
document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend);
},
beforeDestroy() {
this.persistCurrentTabIndex(this.currentTabIndex);
},
destroyed() {
this.composition.off('add', this.addItem);
this.composition.off('remove', this.removeItem);
this.composition.off('reorder', this.onReorder);
this.unsubscribe();
this.clearCurrentTabIndexFromURL();
document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend);
},
methods:{
setCurrentTabByIndex(index) {
if (this.tabsList[index]) {
this.currentTab = this.tabsList[index];
}
},
showTab(tab, index) {
if (index !== undefined) {
this.storeCurrentTabIndex(index);
this.storeCurrentTabIndexInURL(index);
}
this.currentTab = tab;
@ -133,6 +165,10 @@ export default {
this.setCurrentTab = false;
}
},
reset() {
this.currentTab = {};
this.setCurrentTab = true;
},
removeItem(identifier) {
let pos = this.tabsList.findIndex(tab =>
tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
@ -144,6 +180,10 @@ export default {
if (this.isCurrent(tabToBeRemoved)) {
this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
}
if (!this.tabsList.length) {
this.reset();
}
},
onReorder(reorderPlan) {
let oldTabs = this.tabsList.slice();
@ -154,7 +194,7 @@ export default {
},
onDrop(e) {
this.setCurrentTab = true;
this.storeCurrentTabIndex(this.tabsList.length);
this.storeCurrentTabIndexInURL(this.tabsList.length);
},
dragstart(e) {
if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
@ -177,8 +217,19 @@ export default {
updateInternalDomainObject(domainObject) {
this.internalDomainObject = domainObject;
},
storeCurrentTabIndex(index) {
persistCurrentTabIndex(index) {
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
},
storeCurrentTabIndexInURL(index) {
let currentTabIndexInURL = getSearchParam(this.searchTabKey);
if (index !== currentTabIndexInURL) {
setSearchParam(this.searchTabKey, index);
this.currentTabIndex = index;
}
},
clearCurrentTabIndexFromURL() {
deleteSearchParam(this.searchTabKey);
}
}
}

View File

@ -42,20 +42,28 @@ define([
let component;
return {
show: function (element) {
show: function (element, editMode) {
component = new Vue({
el: element,
components: {
TabsComponent: TabsComponent.default
},
data() {
return {
isEditing: editMode
};
},
provide: {
openmct,
domainObject,
composition: openmct.composition.get(domainObject)
},
template: '<tabs-component></tabs-component>'
template: '<tabs-component :isEditing="isEditing"></tabs-component>'
});
},
onEditModeChange(editMode) {
component.isEditing = editMode;
},
destroy: function (element) {
component.$destroy();
component = undefined;

View File

@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(1.2) contrast(1.1);
$filterItemMissing: brightness(0.6) grayscale(1);
$opacityMissing: 0.5;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */
$editUIColor: $uiColor; // Base color

View File

@ -155,6 +155,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(1.2) contrast(1.1);
$filterItemMissing: contrast(0.2);
$opacityMissing: 0.5;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */
$editUIColor: $uiColor; // Base color

View File

@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(0.9);
$filterItemMissing: contrast(0.2);
$opacityMissing: 0.4;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */
$editUIColor: $uiColor; // Base color

View File

@ -61,7 +61,7 @@ $plotXBarH: 35px;
$plotLegendH: 20px;
$plotLegendWidthCollapsed: 20%;
$plotLegendWidthExpanded: 50%;
$plotSwatchD: 10px;
$plotSwatchD: 12px;
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left
$plotMinH: 95px;
$controlBarH: 25px;

View File

@ -420,9 +420,7 @@ select {
margin: 1px 1px 0 0;
padding: $interiorMargin $interiorMarginLg;
white-space: nowrap;
--notchSize: 7px;
clip-path:
polygon(
0% 0%,
@ -433,8 +431,12 @@ select {
0% 100%
);
> * + * {
margin-left: $interiorMargin;
}
@include hover() {
background: $colorBtnBgHov;
filter: $filterHov;
}
&.is-current {

View File

@ -43,7 +43,9 @@ mct-plot {
.c-plot,
.gl-plot {
.s-status-taking-snapshot & {
overflow: hidden;
.s-status-taking-snapshot & {
.c-control-bar {
display: none;
}
@ -51,6 +53,17 @@ mct-plot {
display: none;
}
}
/*********************** MISSING ITEM INDICATORS */
.is-missing__indicator {
display: none;
}
.is-missing {
@include isMissing();
.is-missing__indicator {
font-size: 0.8em;
}
}
}
.c-plot {
@ -74,6 +87,7 @@ mct-plot {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow: hidden;
}
&--stacked {
@ -102,18 +116,17 @@ mct-plot {
}
}
.gl-plot {
display: flex;
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
/*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
position: relative;
flex: 1 1 auto;
min-height: $plotMinH;
}
.gl-plot-wrapper-display-area-and-x-axis {
@ -196,7 +209,6 @@ mct-plot {
left: 0; top: 0; right: auto; bottom: 0;
padding-left: 5px;
text-orientation: mixed;
//overflow: hidden;
writing-mode: vertical-lr;
&:before {
// Icon denoting configurability
@ -368,12 +380,6 @@ mct-plot {
z-index: -10;
.l-view-section {
//$m: $interiorMargin;
//top: $m !important;
//right: $m;
//bottom: $m;
//left: $m;
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: none;
@ -429,14 +435,18 @@ mct-plot {
/****************** _LEGEND.SCSS */
.gl-plot-legend,
.c-plot-legend {
overflow: hidden;
&__wrapper {
// Holds view-control and both collapsed and expanded legends
flex: 1 1 auto;
overflow: auto; // Prevents collapsed legend from forcing scrollbars on higher parent containers
height: 100%;
overflow: auto;
padding: 2px;
}
&__view-control {
padding-top: 2px;
padding-top: 4px;
margin-right: $interiorMarginSm;
}
@ -481,15 +491,21 @@ mct-plot {
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states
> * + * {
margin-left: $interiorMarginSm;
}
.plot-series-color-swatch {
border-radius: $smallCr;
border-radius: 30%; //$smallCr;
border: 1px solid $colorBodyBg;
display: inline-block;
flex: 0 0 auto;
height: $plotSwatchD;
width: $plotSwatchD;
}
.plot-series-name {
display: inline;
@include ellipsize();
}
.plot-series-value {
@ -497,6 +513,16 @@ mct-plot {
}
}
.plot-series-swatch-and-name {
display: flex;
flex: 0 1 auto;
align-items: center;
> * + * {
margin-left: $interiorMarginSm;
}
}
.plot-wrapper-expanded-legend {
flex: 1 1 auto;
}
@ -505,9 +531,6 @@ mct-plot {
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
&.plot-legend-collapsed .icon-cursor-lock::before { padding-right: 5px; }
&.plot-legend-expanded .icon-cursor-lock::before { padding-right: 5px; }
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
@ -521,19 +544,13 @@ mct-plot {
display: flex;
align-items: center;
justify-content: stretch;
&:not(:first-child) {
margin-left: $interiorMarginLg;
}
.plot-series-swatch-and-name,
.plot-series-value {
@include ellipsize();
flex: 1 1 auto;
}
.plot-series-swatch-and-name {
margin-right: $interiorMarginSm;
}
.plot-series-value {
text-align: left;
}
@ -543,7 +560,7 @@ mct-plot {
/***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded {
.gl-plot-legend {
// max-height: 70%;
max-height: 70%;
}
.plot-wrapper-expanded-legend {
@ -564,6 +581,11 @@ mct-plot {
display: flex;
flex: 1 1 auto;
overflow: hidden;
> .plot-legend-item + .plot-legend-item {
// Space between plot items
margin-left: $interiorMarginLg;
}
}
}
}
@ -595,12 +617,17 @@ mct-plot {
min-width: 0;
flex: 1 1 auto;
overflow-y: auto;
> * + * {
// Space between plot items
margin-top: $interiorMarginSm;
}
}
.plot-legend-item {
margin-bottom: 1px;
margin-left: 0;
flex-wrap: wrap;
flex-wrap: nowrap;
.plot-series-swatch-and-name {
@include ellipsize();
flex: 0 1 auto;
min-width: 20%;
}
@ -654,3 +681,24 @@ mct-plot {
display: inline-block !important;
}
}
/*********************** CURSOR LOCK INDICATOR */
[class*='c-state-indicator__alert-cursor-lock'] {
display: none;
}
[class*='is-cursor-locked'] {
background: rgba($colorInfo, 0.1);
[class*='c-state-indicator__alert-cursor-lock'] {
@include userSelectNone();
color: $colorInfo;
display: block;
margin-right: $interiorMarginSm;
&[class*='--verbose'] {
padding: $interiorMarginSm;
}
}
}

View File

@ -117,6 +117,30 @@
}
}
@mixin isMissing($absPos: false) {
// Common styles to be applied to tree items, object labels, grid and list item views
//opacity: 0.7;
//pointer-events: none; // Don't think we can do this, as disables title hover on icon element
.is-missing__indicator {
display: block;
text-shadow: $colorBodyBg 0 0 2px;
color: $colorAlert;
font-family: symbolsfont;
&:before {
content: $glyph-icon-alert-triangle;
}
}
@if $absPos {
.is-missing__indicator {
position: absolute;
z-index: 3;
}
}
}
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
background-image: linear-gradient(-45deg,
rgba($c, $a) 25%, transparent 25%,

View File

@ -49,8 +49,6 @@ table {
td {
vertical-align: top;
}
a { color: $colorBtnMajorBg; }
}
.is-editing {

View File

@ -13,7 +13,6 @@
@import "../plugins/filters/components/filters-view.scss";
@import "../plugins/filters/components/global-filters.scss";
@import "../plugins/flexibleLayout/components/flexible-layout.scss";
@import "../plugins/folderView/components/grid-item.scss";
@import "../plugins/folderView/components/grid-view.scss";
@import "../plugins/folderView/components/list-item.scss";
@import "../plugins/folderView/components/list-view.scss";

View File

@ -24,13 +24,24 @@
class="c-so-view has-local-controls"
:class="{
'c-so-view--no-frame': !hasFrame,
'has-complex-content': complexContent
'has-complex-content': complexContent,
'is-missing': domainObject.status === 'missing'
}"
>
<div class="c-so-view__header">
<div class="c-object-label"
:class="[cssClass, classList]"
:class="{
classList,
'is-missing': domainObject.status === 'missing'
}"
>
<div class="c-object-label__type-icon"
:class="cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-object-label__name">
{{ domainObject && domainObject.name }}
</div>
@ -46,6 +57,9 @@
@click="expand"
></button>
</div>
<div class="is-missing__indicator"
title="This item is missing"
></div>
<object-view
ref="objectView"
class="c-so-view__object-view"

View File

@ -1,7 +1,10 @@
<template>
<a
class="c-tree__item__label c-object-label"
:class="classList"
:class="{
classList,
'is-missing': observedObject.status === 'missing'
}"
draggable="true"
:href="objectLink"
@dragstart="dragStart"
@ -10,8 +13,14 @@
<div
class="c-tree__item__type-icon c-object-label__type-icon"
:class="typeClass"
></div>
<div class="c-tree__item__name c-object-label__name">{{ observedObject.name }}</div>
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-tree__item__name c-object-label__name">
{{ observedObject.name }}
</div>
</a>
</template>

View File

@ -301,7 +301,7 @@ export default {
objectPath= this.currentObjectPath || this.objectPath,
parentObject = objectPath[1];
return [browseObject, parentObject, this.currentObject].every(object => !object.locked);
return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked);
}
}
}

View File

@ -2,6 +2,10 @@
display: flex;
flex-direction: column;
&.is-missing {
border: $borderMissing;
}
/*************************** HEADER */
&__header {
flex: 0 0 auto;
@ -39,6 +43,15 @@
> .c-so-view__local-controls {
top: $interiorMarginSm; right: $interiorMarginSm;
}
&.is-missing {
@include isMissing($absPos: true);
.is-missing__indicator {
top: $interiorMargin;
left: $interiorMargin;
}
}
}
&__local-controls {

View File

@ -3,23 +3,38 @@
// Used mostly in trees and lists
display: flex;
align-items: center;
flex: 0 1 auto;
flex: 1 1 auto;
overflow: hidden;
white-space: nowrap;
> * + * { margin-left: $interiorMargin; }
&__name {
@include ellipsize();
display: inline;
}
&__type-icon,
&:before {
&__type-icon {
// Type icon. Must be an HTML entity to allow inclusion of alias indicator.
display: block;
flex: 0 0 auto;
font-size: 1.1em;
opacity: 0.6;
margin-right: $interiorMargin;
//margin-right: $interiorMargin;
}
&.is-missing {
@include isMissing($absPos: true);
[class*='__type-icon']:before,
[class*='__type-icon']:after{
opacity: $opacityMissing;
}
.is-missing__indicator {
right: -3px;
top: -3px;
transform: scale(0.7);
}
}
}
@ -27,6 +42,8 @@
border-radius: $controlCr;
padding: $interiorMarginSm 1px;
> * + * { margin-left: $interiorMarginSm; }
&__name {
display: inline;
width: 100%;

View File

@ -1,16 +1,24 @@
<template>
<div class="c-inspector__header">
<div v-if="!multiSelect && !singleSelectNonObject"
<div v-if="!multiSelect"
class="c-inspector__selected-w c-object-label"
:class="typeCssClass"
:class="{'is-missing': domainObject.status === 'missing' }"
>
<span class="c-inspector__selected c-object-label__name">{{ item.name }}</span>
</div>
<div v-if="singleSelectNonObject"
class="c-inspector__selected-w c-object-label"
:class="typeCssClass"
>
<span class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object">Layout Object</span>
<div class="c-object-label__type-icon"
:class="typeCssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<span v-if="!singleSelectNonObject"
class="c-inspector__selected c-object-label__name"
>{{ item.name }}</span>
<span v-if="singleSelectNonObject"
class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object"
>Layout Object</span>
</div>
<div v-if="multiSelect"
class="c-inspector__multiple-selected-w"

View File

@ -8,8 +8,18 @@
></button>
<div
class="l-browse-bar__object-name--w c-object-label"
:class="[ type.cssClass, classList ]"
:class="{
classList,
'is-missing': domainObject.status === 'missing'
}"
>
<div class="c-object-label__type-icon"
:class="type.cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<span
class="l-browse-bar__object-name c-object-label__name c-input-inline"
contenteditable

View File

@ -354,9 +354,9 @@
@include headerFont(1.4em);
min-width: 0;
&:before {
// Icon
margin-right: $interiorMargin;
.is-missing__indicator {
right: -5px !important;
top: -4px !important;
}
}

View File

@ -52,12 +52,13 @@
padding: $interiorMarginSm $interiorMargin;
transition: background 150ms ease;
&__type-icon {
color: $colorItemTreeIcon;
}
&:hover {
background: $colorItemTreeHoverBg;
[class*="__name"] {
color: $colorItemTreeHoverFg;
}
filter: $filterHov;
}
&.is-navigated-object,
@ -81,12 +82,6 @@
margin-left: $interiorMarginSm;
}
&:hover {
.c-tree__item__type-icon:before {
color: $colorItemTreeIconHover;
}
}
&.is-navigated-object,
&.is-selected {
.c-tree__item__type-icon:before {
@ -115,10 +110,6 @@
color: $colorItemTreeFg;
}
&__type-icon {
color: $colorItemTreeIcon;
}
&.is-alias {
// Object is an alias to an original.
[class*='__type-icon'] {