diff --git a/src/plugins/displayLayout/DisplayLayoutType.js b/src/plugins/displayLayout/DisplayLayoutType.js index 66b34d6c17..0d3b8b84f9 100644 --- a/src/plugins/displayLayout/DisplayLayoutType.js +++ b/src/plugins/displayLayout/DisplayLayoutType.js @@ -25,6 +25,7 @@ define(function () { return { name: "Display Layout", creatable: true, + description: 'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.', cssClass: 'icon-layout', initialize(domainObject) { domainObject.composition = []; diff --git a/src/plugins/displayLayout/components/DisplayLayout.vue b/src/plugins/displayLayout/components/DisplayLayout.vue index 76a9fb6199..055e747e93 100644 --- a/src/plugins/displayLayout/components/DisplayLayout.vue +++ b/src/plugins/displayLayout/components/DisplayLayout.vue @@ -25,30 +25,26 @@ @dragover="handleDragOver" @click="bypassSelection" @drop="handleDrop"> -
- -
-
-
-
+ +
+
- - +
+ +
@@ -59,30 +55,47 @@ @include abs(); display: flex; flex-direction: column; + overflow: auto; &__grid-holder { display: none; } - &__object { - flex: 1 1 auto; - overflow: auto; - } - &__frame { position: absolute; } } - .l-shell__main-container { - > .l-layout { - [s-selected] { - border: $browseSelectedBorder; + .is-editing { + .l-shell__main-container { + &[s-selected], + &[s-selected-parent] { + // Display grid in main layout holder when editing + > .l-layout { + background: $editUIGridColorBg; + + > [class*="__grid-holder"] { + display: block; + } + } + } + } + + .l-layout__frame { + &[s-selected], + &[s-selected-parent] { + // Display grid in nested layouts when editing + > * > * > .l-layout { + background: $editUIGridColorBg; + box-shadow: inset $editUIGridColorFg 0 0 2px 1px; + + > [class*='grid-holder'] { + display: block; + } + } } } } - - // Styles moved to _global.scss; @@ -119,7 +132,6 @@ data() { let domainObject = JSON.parse(JSON.stringify(this.domainObject)); return { - drilledIn: undefined, internalDomainObject: domainObject, initSelectIndex: undefined }; @@ -275,12 +287,17 @@ this.initSelectIndex = this.layoutItems.length - 1; }, trackItem(item) { + if (!item.identifier) { + return; + } + + let keyString = this.openmct.objects.makeKeyString(item.identifier); + if (item.type === "telemetry-view") { - let keyString = this.openmct.objects.makeKeyString(item.identifier); let count = this.telemetryViewMap[keyString] || 0; this.telemetryViewMap[keyString] = ++count; } else if (item.type === "subobject-view") { - this.objectViewMap[this.openmct.objects.makeKeyString(item.identifier)] = true; + this.objectViewMap[keyString] = true; } }, removeItem(item, index) { @@ -376,5 +393,4 @@ } } } - diff --git a/src/plugins/displayLayout/components/LayoutFrame.vue b/src/plugins/displayLayout/components/LayoutFrame.vue index 2560aa7393..d0820ee71f 100644 --- a/src/plugins/displayLayout/components/LayoutFrame.vue +++ b/src/plugins/displayLayout/components/LayoutFrame.vue @@ -25,25 +25,24 @@ :class="{ 'no-frame': !item.hasFrame, 'u-inspectable': inspectable, - 'is-drilled-in': item.drilledIn + 'is-resizing': isResizing }" - :style="style" - @dblclick="drill($event)"> + :style="style">
+ @mousedown="startDrag([1,1], [0,0], $event, 'move')">
+ @mousedown="startDrag([1,1], [-1,-1], $event, 'resize')">
+ @mousedown="startDrag([0,1], [1,-1], $event, 'resize')">
+ @mousedown="startDrag([1,0], [-1,1], $event, 'resize')">
+ @mousedown="startDrag([0,0], [1,1], $event, 'resize')"> @@ -55,20 +54,167 @@ .c-frame { display: flex; flex-direction: column; - border: 1px solid transparent; - /*************************** NO-FRAME */ - &.no-frame { - > [class*="contents"] > [class*="__header"] { - display: none; - } + // Whatever is placed into the slot, make it fill the entirety of the space, obeying padding + > *:first-child { + flex: 1 1 auto; } &:not(.no-frame) { background: $colorBodyBg; - border: 1px solid $colorInteriorBorder; + border: $browseFrameBorder; padding: $interiorMargin; } + + } + + .c-frame-edit { + // In Layouts, this is the editing rect and handles + // In Fixed Position, this is a wrapper element + @include abs(); + display: none; + + &__move { + @include abs(); + cursor: move; + } + + &__handle { + $d: 6px; + $o: floor($d * -0.5); + background: $editFrameColorHandleFg; + box-shadow: $editFrameColorHandleBg 0 0 0 2px; + display: none; // Set to block via s-selected selector + position: absolute; + width: $d; height: $d; + top: auto; right: auto; bottom: auto; left: auto; + + &:before { + // Extended hit area + @include abs(-10px); + content: ''; + display: block; + z-index: 0; + } + + &:hover { + background: $editUIColor; + } + + &--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; + } + } + } + + .c-so-view.has-complex-content + .c-frame-edit { + // Target frames that hold domain objects that include header elements, as opposed to drawing and alpha objects + // Make the __move element a more affordable drag UI element + .c-frame-edit__move { + @include userSelectNone(); + background: $editFrameMovebarColorBg; + box-shadow: rgba(black, 0.2) 0 1px; + bottom: auto; + height: 0; // Height is set on hover on s-selected.c-frame + opacity: 0.8; + max-height: 100%; + overflow: hidden; + text-align: center; + + &:before { + // Grippy + $h: 4px; + $tbOffset: ($editFrameMovebarH - $h) / 2; + $lrOffset: 25%; + @include grippy($editFrameMovebarColorFg); + content: ''; + display: block; + position: absolute; + top: $tbOffset; right: $lrOffset; bottom: $tbOffset; left: $lrOffset; + } + + &:hover { + background: $editFrameHovMovebarColorBg; + &:before { @include grippy($editFrameHovMovebarColorFg); } + } + } + } + + .is-editing { + .c-frame { + $moveBarOutDelay: 500ms; + &.no-frame { + border: $editFrameBorder; // Base border style for a frame element while editing. + } + + &-edit { + display: contents; + } + + &-edit__move, + .c-so-view { + transition: $transOut; + transition-delay: $moveBarOutDelay; + } + + &:not([s-selected]) { + &:hover { + border: $editFrameBorderHov; + } + } + + &[s-selected] { + // All frames selected while editing + border: $editFrameSelectedBorder; + box-shadow: $editFrameSelectedShdw; + + > .c-frame-edit { + [class*='__handle'] { + display: block; + } + } + } + } + + .l-layout__frame:not(.is-resizing) { + // Show and animate the __move bar for sub-object views with complex content + &:hover > .c-so-view.has-complex-content { + // Move content down so the __move bar doesn't cover it. + padding-top: $editFrameMovebarH; + transition: $transIn; + + &.c-so-view--no-frame { + // Move content down with a bit more space + padding-top: $editFrameMovebarH + $interiorMarginSm; + } + + // Show the move bar + + .c-frame-edit .c-frame-edit__move { + height: $editFrameMovebarH; + transition: $transIn; + } + } + } } @@ -84,7 +230,8 @@ }, data() { return { - dragPosition: undefined + dragPosition: undefined, + isResizing: undefined } }, computed: { @@ -110,24 +257,6 @@ } }, methods: { - drill($event) { - if ($event) { - $event.stopPropagation(); - } - - if (!this.openmct.editor.isEditing() || !this.item.identifier) { - return; - } - - this.openmct.objects.get(this.item.identifier) - .then(domainObject => { - if (this.openmct.composition.get(domainObject) === undefined) { - return; - } - - this.$emit('drilledIn', this.item); - }); - }, updatePosition(event) { let currentPosition = [event.pageX, event.pageY]; this.initialPosition = this.initialPosition || currentPosition; @@ -135,7 +264,7 @@ return value - this.initialPosition[index]; }.bind(this)); }, - startDrag(posFactor, dimFactor, event) { + startDrag(posFactor, dimFactor, event, type) { document.body.addEventListener('mousemove', this.continueDrag); document.body.addEventListener('mouseup', this.endDrag); @@ -145,6 +274,7 @@ }; this.updatePosition(event); this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize); + this.isResizing = type === 'resize'; event.preventDefault(); }, continueDrag(event) { @@ -162,6 +292,7 @@ this.dragPosition = undefined; this.initialPosition = undefined; this.delta = undefined; + this.isResizing = undefined; event.preventDefault(); } } diff --git a/src/plugins/displayLayout/components/SubobjectView.vue b/src/plugins/displayLayout/components/SubobjectView.vue index 5a9df42b89..17e42ff0ec 100644 --- a/src/plugins/displayLayout/components/SubobjectView.vue +++ b/src/plugins/displayLayout/components/SubobjectView.vue @@ -22,8 +22,7 @@