Compare commits

...

46 Commits

Author SHA1 Message Date
Charles Hacskaylo
728faea4ce [Frontend] Hover border sanding and polishing
Fixes #1658
Prevent hover border from showing when nested;
Show hover border for all elements when Layout editing;
2017-08-17 14:49:49 -07:00
Charles Hacskaylo
59db676fd8 [Frontend] Disallow selection of nested frames
Fixes #1658
2017-08-17 12:04:21 -07:00
Pegah Sarram
f89c74dfa3 Added a test for the case where selection should not be cleared when moving/resizing. 2017-08-17 10:52:23 -07:00
Pegah Sarram
60ece16c33 Fixed lint and checkstyle errors. 2017-08-17 10:12:28 -07:00
Pegah Sarram
7dd7421890 Removed unnecessary comments and clarified code by using toBe(true). 2017-08-16 17:23:15 -07:00
Pegah Sarram
6d6124820d Set a default value on hasFrame and made sure 'hyperlink' objects have no frame by default. 2017-08-16 16:59:33 -07:00
Pegah Sarram
1b334086b6 Merge remote-tracking branch 'origin' into layout-issue-1658 2017-08-16 16:03:29 -07:00
Pegah Sarram
d725e30481 Merge remote-tracking branch 'origin/layout-issue-1658-styling' into layout-issue-1658 2017-08-15 16:35:30 -07:00
Pegah Sarram
5b26264409 Added tests for selecting an object and clearing selection. 2017-08-15 16:34:52 -07:00
Charles Hacskaylo
8466ba51a0 [Front-end] Tweaks to View Large button and overlay
Fixes #1658
Tweaks to Large View overlay template;
Now displays Large View button in nested Layout within Large View;
CSS tweaks;
2017-08-15 10:41:03 -07:00
Charles Hacskaylo
c2ed84d7ca [Front-end] Enable View Large when frame is hidden
Fixes #1658
View Large button and view Switcher now
displayed on hover when frame is hidden for a Layout child object;
Moved location of View Large button in markup;
Tweaked location of view Switcher in markup;
CSS mods accordingly;
2017-08-14 17:08:14 -07:00
Pegah Sarram
425fbc91e2 Merge remote-tracking branch 'origin/layout-issue-1658-styling' into layout-issue-1658 2017-08-14 16:09:45 -07:00
Pegah Sarram
3dd36e7ce8 Merge remote-tracking branch 'origin' into layout-issue-1658 2017-08-14 16:08:31 -07:00
Pegah Sarram
9925c2dd38 Fixed the brokend tests. 2017-08-14 16:07:52 -07:00
Charles Hacskaylo
0239faa9a8 [Front-end] CSS organization
Fixes #1658
Moved grid CSS into editor.scss
2017-08-14 15:37:38 -07:00
Charles Hacskaylo
e802b5344c [Front-end] Added visual layout grid to Layouts
Fixes #1658
Hides grid when grid size is less than 3 px;
2017-08-14 15:03:35 -07:00
Charles Hacskaylo
b5f196ede8 [Front-end] Bring over Hyperlink styling
Fixes #1658
Change icon for Hyperlink (cherry picked from commit f5a92f6)
2017-08-14 12:49:02 -07:00
Charles Hacskaylo
dbc7c910cc [Front-end] Bring over Hyperlink styling
Fixes #1658
(cherry picked from commit 0417b7e)
2017-08-14 12:48:33 -07:00
Charles Hacskaylo
0acf8d9edc [Front-end] Prevernt Hyperlink click when editing
Fixes #1685
Prevent Hyperlink object from being clickable
when in Edit mode;
2017-08-14 12:06:57 -07:00
Charles Hacskaylo
af5d42da1e Merge branch 'layout-issue-1658' of https://github.com/nasa/openmct into layout-issue-1658-styling 2017-08-14 11:57:58 -07:00
Charles Hacskaylo
4dff369807 [Front-end] Integrate Hyperlink
Fixes #1685
Fixed problematic markup in hyperlink.html;
Styling for Hyperlink in Layout context;
Tweaks to no-frame style;
2017-08-14 11:57:49 -07:00
Pegah Sarram
eb999ad37b Removed the resize parameter and defined a flag and set a timeout instead to clear it. 2017-08-14 11:35:58 -07:00
Charles Hacskaylo
36a0976e98 Merge branch 'layout-issue-1658' of https://github.com/nasa/openmct into layout-issue-1658-styling 2017-08-14 10:53:54 -07:00
Pegah Sarram
cbc2555687 Set a flag when frame is resized so clearSelection() can handle and keep the selection. 2017-08-14 10:41:00 -07:00
Charles Hacskaylo
e040abc329 Merge branch 'master' into layout-issue-1658-styling 2017-08-14 10:35:00 -07:00
Pegah Sarram
d5ca720eff Merge remote-tracking branch 'origin/layout-issue-1658-styling' into layout-issue-1658 2017-08-11 15:23:53 -07:00
Charles Hacskaylo
d605b2ce43 [Frontend] Fixed view-large btn
Fixes #1658
Messing up hover on frame element,
t-edit-handle was displaying in browse mode -
now hide by default and only show in edit-mode
2017-08-11 14:58:37 -07:00
Charles Hacskaylo
d895b47bc6 [Frontend] Cherry pick over new glyphs
Fixes #1658
Updated art for glyphs for
icon-frame-show and -hide;
(cherry picked from commit 43b9264)
2017-08-11 14:37:47 -07:00
Pegah Sarram
33efe77172 Merge remote-tracking branches 'origin' and 'origin/layout-issue-1658-styling' into layout-issue-1658 2017-08-11 14:28:44 -07:00
Charles Hacskaylo
bbd5dfe335 [Frontend] Added no-frame styling
Fixes #1658
Markup and CSS for no-frame;
Removed no-frame parameters passed
to frame.html;
2017-08-11 14:02:48 -07:00
Pegah Sarram
71a1b46d69 Disallow moving/resizing before selecting the frame. 2017-08-11 13:55:05 -07:00
Charles Hacskaylo
d86ce9794e [Frontend] Tweaked FP bg grid
Fixes #1658
2017-08-11 13:15:31 -07:00
Charles Hacskaylo
c2d2cbfa1e [Frontend] Selecting in Layouts and Fixed Position
Fixes #1658
WIP; CSS cleanup; sanding and polishing;
2017-08-11 12:01:25 -07:00
Charles Hacskaylo
f384ca3f53 [Frontend] Selecting in Layouts and Fixed Position
Fixes #1658
WIP!
Finessed styles and markup; only show move cursor when
object is selected;
2017-08-11 11:31:54 -07:00
Charles Hacskaylo
ce858ca598 [Frontend] Major changes re. selecting in Layouts and Fixed Position
Fixes #1658
WIP!
Significant changes to markup and CSS for .s-selected,
.s-selectable, etc.
Applied to Layouts and Fixed Position objects;
Restored bg grid to FP objects;
Possibly breaks Panels at this point;
TO-DO: checkout Panels and fix if needed, remove
commented CSS code;
2017-08-10 18:28:37 -07:00
Charles Hacskaylo
6df5de9cce Merge branch 'layout-issue-1658' of https://github.com/nasa/openmct into layout-issue-1658-styling 2017-08-10 14:54:57 -07:00
Pegah Sarram
9f59b1920b Merge branch 'master' into layout-issue-1658 2017-08-10 14:53:35 -07:00
Charles Hacskaylo
f778f73bfc Merge branch 'master' into layout-issue-1658-styling 2017-08-10 14:52:38 -07:00
Pegah Sarram
63579668bd Added code to hide the object header when the frame is hidden. Also, replace the inline style with the 'abs' css class. 2017-08-10 14:37:04 -07:00
Pegah Sarram
9087147dda Added code to select the newly-added object. 2017-08-09 16:20:19 -07:00
Pegah Sarram
0d61d70b8c Swapped the css class for showing/hiding frame. 2017-08-09 15:33:11 -07:00
Pegah Sarram
e1e2fc5bcd Merge remote-tracking branch 'refs/remotes/origin/layout-issue-1658' into layout-issue-1658 2017-08-09 13:28:51 -07:00
Pegah Sarram
5a3b98c731 Removed unused reselect(). 2017-08-09 13:14:07 -07:00
Pete Richards
df01646954 make new selection object on toggle
Make and select a new object every time the frame is toggled.  This
ensures that the EditToolbarRepresenter detects the change of selection
and updates it's display state.
2017-08-09 10:28:42 -07:00
Pegah Sarram
a986ddddcc Added a mutation listener. 2017-08-08 16:05:22 -07:00
Sarram
c8eba1fbad Added selection capability for the panels in layout. Also, added a toggle button in the toolbar to allow showing/hiding frame around the panels. 2017-08-08 10:53:17 -07:00
14 changed files with 392 additions and 179 deletions

View File

@@ -28,8 +28,4 @@
key="'menu-arrow'" key="'menu-arrow'"
mct-object='domainObject' mct-object='domainObject'
class="flex-elem context-available-w"></mct-representation> class="flex-elem context-available-w"></mct-representation>
</span> </span>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-trigger-modal>
</a>

View File

@@ -21,32 +21,11 @@
*****************************************************************************/ *****************************************************************************/
.t-fixed-position { .t-fixed-position {
&.l-fixed-position { &.l-fixed-position {
position: absolute; @extend .abs;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: auto;
height: auto;
.l-grid-holder {
position: relative;
height: 100%;
width: 100%;
.l-grid {
position: absolute;
height: 100%;
width: 100%;
pointer-events: none;
z-index: 0;
}
}
} }
.l-fixed-position-item { .l-fixed-position-item {
position: absolute; position: absolute;
border: 1px solid transparent;
&.s-not-selected { &.s-not-selected {
opacity: 0.8; opacity: 0.8;
} }
@@ -105,37 +84,8 @@
} }
} }
} }
.l-fixed-position-item-handle {
$brd: 1px solid $colorKey;
background: rgba($colorKey, 0.5);
cursor: crosshair;
border: $brd;
position: absolute;
}
} }
.edit-mode .t-fixed-position { .s-status-editing {
&.l-fixed-position { .l-fixed-position-item-handle.edit-corner { display: block; }
.l-grid-holder {
.l-grid {
&.l-grid-x {
@include bgTicks($colorGridLines, 'x');
}
&.l-grid-y {
@include bgTicks($colorGridLines, 'y');
}
}
}
}
.l-fixed-position-item {
&:not(.s-selected) {
border: 1px dotted rgba($colorKey, 0.75);
&:hover {
border: 1px dotted rgba($colorKey, 1.0);
}
}
}
} }

View File

@@ -704,11 +704,6 @@ textarea {
} }
} }
.view-switcher,
.t-btn-view-large {
@include trans-prop-nice-fade($controlFadeMs);
}
/******************************************************** BROWSER ELEMENTS */ /******************************************************** BROWSER ELEMENTS */
body.desktop { body.desktop {
::-webkit-scrollbar { ::-webkit-scrollbar {

View File

@@ -19,12 +19,24 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.s-status-editing .l-object-wrapper, .t-edit-handle-holder { display: none; }
.edit-main {
// .s-status-editing .l-object-wrapper is relevant to New Edit Mode; .l-grid-holder {
// .edit-main is legacy for old edit mode. display: none;
$handleD: 15px; position: relative;
$cr: 5px; height: 100%;
width: 100%;
.l-grid {
@extend .abs;
pointer-events: none;
z-index: 0;
&.l-grid-y { background-position: 0 1px; }
}
}
.s-status-editing {
$handleD: 5px;
.t-edit-handle-holder { display: block; }
.edit-corner, .edit-corner,
.edit-handle { .edit-handle {
position: absolute; position: absolute;
@@ -32,81 +44,59 @@
} }
.edit-corner { .edit-corner {
background: rgba($colorSelectableSelectedPrimary, 0.5);
cursor: crosshair;
display: none; // Hide by default
border: 1px solid $colorSelectableSelectedPrimary;
width: $handleD; width: $handleD;
height: $handleD; height: $handleD;
$o: (-1 * $handleD) + 1px;
&:hover { &:hover {
z-index: 11; z-index: 11;
} }
&.edit-resize-nw { &.edit-resize-nw { top: $o; left: $o; }
border-bottom-right-radius: $cr; &.edit-resize-ne { top: $o; right: $o; }
cursor: nw-resize; &.edit-resize-se { bottom: $o; right: $o; }
top: 0; left: 0; &.edit-resize-sw { bottom: $o; left: $o; }
}
&.edit-resize-ne {
border-bottom-left-radius: $cr;
cursor: ne-resize;
top: 0; right: 0;
}
&.edit-resize-se {
border-top-left-radius: $cr;
cursor: se-resize;
bottom: 0; right: 0;
}
&.edit-resize-sw {
border-top-right-radius: $cr;
cursor: sw-resize;
bottom: 0; left: 0;
}
} }
.edit-handle { .edit-handle.edit-move {
top: $handleD; right: $handleD; bottom: $handleD; left: $handleD; // main move box for the whole frame element
&.edit-move { $m: 0;
$m: 0; //$handleD; left: $m;
cursor: move; right: $m;
left: $m; top: $m;
right: $m; bottom: $m;
top: $m; z-index: 1;
bottom: $m;
z-index: 1;
}
&.edit-resize-n {
top: 0px; bottom: auto;
height: $handleD;
cursor: n-resize;
}
&.edit-resize-e {
right: 0px; left: auto;
width: $handleD;
cursor: e-resize;
}
&.edit-resize-s {
bottom: 0px; top: auto;
height: $handleD;
cursor: s-resize;
}
&.edit-resize-w {
left: 0px; right: auto;
width: $handleD;
cursor: w-resize;
}
} }
.frame.child-frame.panel { .frame.child-frame.panel {
&:hover { &:hover {
@include boxShdwLarge();
border-color: $colorSelectableSelectedPrimary;
.view-switcher { .view-switcher {
opacity: 1; opacity: 1;
} }
.edit-corner {
background-color: rgba($colorKey, 0.8);
&:hover {
background-color: rgba($colorKey, 1);
}
}
} }
} }
// Editing Grids
.l-grid-holder {
display: block;
.l-grid {
&.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
&.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
}
}
// Prevent nested frames from showing their grids
.t-frame-outer .l-grid-holder { display: none !important; }
// Prevent nested elements from showing s-hover-border
.t-frame-outer .s-hover-border {
border: none !important;
}
// Prevent nested frames from being selectable until we have proper sub-object editing
.t-frame-outer .t-frame-outer {
pointer-events: none;
}
} }

View File

@@ -193,6 +193,7 @@
@include animToParams(overlayIn, $dur: $durLargeViewExpand, $delay: 0); @include animToParams(overlayIn, $dur: $durLargeViewExpand, $delay: 0);
background: $colorBodyBg; background: $colorBodyBg;
z-index: 101;
.abs.inner-holder { .abs.inner-holder {
opacity: 0; opacity: 0;
@@ -203,10 +204,19 @@
@include animToParams(contentsIn, $dur: 50ms, $delay: $durLargeViewExpand * 1.25); @include animToParams(contentsIn, $dur: 50ms, $delay: $durLargeViewExpand * 1.25);
} }
// Hide View Large button
.t-btn-view-large { .t-btn-view-large {
display: none; display: none;
} }
z-index: 101;
// But show View Large button when it's nested inside a Layout
.t-frame-inner .t-frame-inner .t-btn-view-large { display: block; }
}
}
// When multiple Large Views are visible, hide the blocker for all but the first
& + .l-large-view {
.blocker {
display: none;
} }
} }
} }

View File

@@ -23,28 +23,43 @@
$ohH: $btnFrameH; $ohH: $btnFrameH;
$bc: $colorInteriorBorder; $bc: $colorInteriorBorder;
&.child-frame.panel { &.child-frame.panel {
background: $colorBodyBg;
border: 1px solid $bc;
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
&:hover { &:not(.no-frame) {
border-color: lighten($bc, 10%); background: $colorBodyBg;
border: 1px solid $bc;
&:hover {
border-color: lighten($bc, 10%);
}
} }
} }
.object-browse-bar { .object-browse-bar {
font-size: 0.75em; font-size: 0.75em;
height: $ohH; height: $ohH;
line-height: $ohH; line-height: $ohH;
.right {
@include trans-prop-nice-fade($controlFadeMs);
padding-left: $interiorMargin;
}
}
&.t-object-type-timer,
&.t-object-type-clock,
&.t-object-type-hyperlink {
// Hide the right side buttons for objects where they don't make sense
// Note that this will hide the view Switcher button if applied
// to an object that it.
.object-browse-bar .right { display: none; }
} }
> .object-holder.abs { > .object-holder.abs {
top: $ohH + $interiorMargin; top: $ohH + $interiorMargin;
} }
.contents { .contents {
$myM: $interiorMargin; $m: $interiorMargin;
top: $myM; top: $m;
right: $myM; right: $m;
bottom: $myM; bottom: $m;
left: $myM; left: $m;
} }
&.frame-template { &.frame-template {
.s-button, .s-button,
@@ -67,15 +82,25 @@
} }
} }
.view-switcher { .view-switcher {
margin-left: $interiorMargin; // Kick other top bar elements away when I'm present. margin-right: $interiorMargin; // Kick other top bar elements away when I'm present.
// Hide the name when the view switcher is in a frame context // Hide the name when the view switcher is in a frame context
.title-label { .title-label {
display: none; display: none;
} }
} }
&.no-frame { &.no-frame {
background: transparent !important; background: transparent !important;
border: none !important; border: none !important;
.object-browse-bar .right {
$m: 0; // $interiorMarginSm;
background: rgba(black, 0.3);
border-radius: $basicCr;
padding: $interiorMarginSm;
position: absolute;
top: $m; right: $m;
z-index: 2;
}
&.t-frame-outer > .t-rep-frame { &.t-frame-outer > .t-rep-frame {
&.contents { &.contents {
$m: 2px; $m: 2px;
@@ -85,7 +110,7 @@
left: $m; left: $m;
} }
> .t-frame-inner { > .t-frame-inner {
> .object-browse-bar { > .object-browse-bar .left {
display: none; display: none;
} }
> .object-holder.abs { > .object-holder.abs {
@@ -117,8 +142,7 @@
body.desktop .frame { body.desktop .frame {
// Hide local controls initially and show it them on hover when they're in an element that's in a frame context // Hide local controls initially and show it them on hover when they're in an element that's in a frame context
// Frame template is used because we need to target the lowest nested frame // Frame template is used because we need to target the lowest nested frame
.view-switcher, .right {
.t-btn-view-large {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
@@ -126,8 +150,7 @@ body.desktop .frame {
// Target the first descendant so that we only show the elements in the outermost container. // Target the first descendant so that we only show the elements in the outermost container.
// Handles the case where we have layouts in layouts. // Handles the case where we have layouts in layouts.
&:hover > .object-browse-bar { &:hover > .object-browse-bar {
.view-switcher, .right {
.t-btn-view-large {
opacity: 1; opacity: 1;
pointer-events: inherit; pointer-events: inherit;
} }

View File

@@ -19,21 +19,36 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.s-selectable { .s-hover-border {
border: 1px solid transparent; border: 1px dotted transparent;
}
&.s-hover { .s-status-editing {
// Styles when hovering over a selectable object // Limit to editing mode until we have sub-object selection
border-color: $colorSelectableHov !important; .s-hover-border {
// Show a border by default so user can see object bounds and empty objects
border: 1px dotted rgba($colorSelectableSelectedPrimary, 0.3) !important;
&:hover {
border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important;
}
} }
&.s-selected { .s-selected > .s-hover-border,
.s-selected.s-hover-border {
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects. // Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
border-color: $colorSelectableSelectedPrimary !important; border-color: $colorSelectableSelectedPrimary !important;
@include boxShdwLarge();
// Show edit-corners if you got 'em
.edit-corner {
display: block;
&:hover {
background-color: rgba($colorKey, 1);
}
}
} }
&.s-moveable { .s-selected > .s-moveable,
@include boxShdwLarge(); .s-selected.s-moveable {
cursor: move; cursor: move;
} }
} }

View File

@@ -62,7 +62,29 @@ define([
"type": "layout", "type": "layout",
"template": layoutTemplate, "template": layoutTemplate,
"editable": true, "editable": true,
"uses": [] "uses": [],
"toolbar": {
"sections": [
{
"items": [
{
"method": "showFrame",
"cssClass": "icon-frame-show",
"control": "button",
"title": "Show frame",
"description": "Show frame"
},
{
"method": "hideFrame",
"cssClass": "icon-frame-hide",
"control": "button",
"title": "Hide frame",
"description": "Hide frame"
}
]
}
]
}
}, },
{ {
"key": "fixed", "key": "fixed",

View File

@@ -25,15 +25,20 @@
<!-- Background grid --> <!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()"> <div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x" <div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div> ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y" <div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div> ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div> </div>
<!-- Fixed position elements --> <!-- Fixed position elements -->
<div ng-repeat="element in controller.getElements()" <div ng-repeat="element in controller.getElements()"
class="l-fixed-position-item" class="l-fixed-position-item s-selectable s-moveable s-hover-border"
ng-class="{ 's-not-selected': controller.selected() && !controller.selected(element) }" ng-class="{
's-not-selected': controller.selected() && !controller.selected(element),
's-selected': controller.selected(element)
}"
ng-style="element.style" ng-style="element.style"
ng-click="controller.select(element)"> ng-click="controller.select(element)">
<mct-include key="element.template" <mct-include key="element.template"
@@ -43,15 +48,15 @@
</div> </div>
<!-- Selection highlight, handles --> <!-- Selection highlight, handles -->
<span ng-if="controller.selected()"> <span class="s-selected s-moveable" ng-if="controller.selected()">
<div class="l-fixed-position-item s-selectable s-selected s-moveable" <div class="l-fixed-position-item t-edit-handle-holder"
mct-drag-down="controller.moveHandle().startDrag(controller.selected())" mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
mct-drag="controller.moveHandle().continueDrag(delta)" mct-drag="controller.moveHandle().continueDrag(delta)"
mct-drag-up="controller.moveHandle().endDrag()" mct-drag-up="controller.moveHandle().endDrag()"
ng-style="controller.selected().style"> ng-style="controller.selected().style">
</div> </div>
<div ng-repeat="handle in controller.handles()" <div ng-repeat="handle in controller.handles()"
class="l-fixed-position-item-handle" class="l-fixed-position-item-handle edit-corner"
ng-style="handle.style()" ng-style="handle.style()"
mct-drag-down="handle.startDrag()" mct-drag-down="handle.startDrag()"
mct-drag="handle.continueDrag(delta)" mct-drag="handle.continueDrag(delta)"

View File

@@ -29,15 +29,21 @@
</mct-representation> </mct-representation>
</div> </div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed"> <div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation key="'switcher'" <mct-representation
ng-model="representation" key="'switcher'"
mct-object="domainObject"> ng-model="representation"
mct-object="domainObject">
</mct-representation> </mct-representation>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-trigger-modal>
</a>
</div> </div>
</div> </div>
<div class="abs object-holder"> <div class="abs object-holder">
<mct-representation key="representation.selected.key" <mct-representation
mct-object="representation.selected.key && domainObject"> key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation> </mct-representation>
</div> </div>
</div> </div>

View File

@@ -19,19 +19,33 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="l-layout"
ng-controller="LayoutController as controller">
<div class='frame child-frame panel abs' <div class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition" ng-repeat="childObject in composition"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())"> ng-style="controller.getFrameStyle(childObject.getId())">
<mct-representation key="'frame'" <mct-representation key="'frame'"
class="frame child-frame holder contents abs" class="t-rep-frame holder contents abs"
mct-object="childObject"> mct-object="childObject">
</mct-representation> </mct-representation>
<!-- Drag handles --> <!-- Drag handles -->
<span ng-show="domainObject.hasCapability('editor')"> <span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move" <span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])" mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)" mct-drag="controller.continueDrag(delta)"

View File

@@ -33,6 +33,9 @@ define(
DEFAULT_GRID_SIZE = [32, 32], DEFAULT_GRID_SIZE = [32, 32],
MINIMUM_FRAME_SIZE = [320, 180]; MINIMUM_FRAME_SIZE = [320, 180];
// Method names to expose from this controller
var HIDE = 'hideFrame', SHOW = 'showFrame';
/** /**
* The LayoutController is responsible for supporting the * The LayoutController is responsible for supporting the
* Layout view. It arranges frames according to saved configuration * Layout view. It arranges frames according to saved configuration
@@ -79,6 +82,11 @@ define(
], ],
dimensions: self.defaultDimensions() dimensions: self.defaultDimensions()
}; };
// Store the id so that the newly-dropped object
// gets selected during refresh composition
self.droppedFrameId = id;
// Mark change as persistable // Mark change as persistable
if ($scope.commit) { if ($scope.commit) {
$scope.commit("Dropped a frame."); $scope.commit("Dropped a frame.");
@@ -111,6 +119,13 @@ define(
$scope.composition = composition; $scope.composition = composition;
self.layoutPanels(ids); self.layoutPanels(ids);
self.setDefaultFrame();
// If there is a newly-dropped object, select it.
if (self.droppedFrameId) {
self.select(null, self.droppedFrameId);
delete self.droppedFrameId;
}
} }
}); });
} }
@@ -123,13 +138,21 @@ define(
// saved by the EditRepresenter. // saved by the EditRepresenter.
$scope.configuration = $scope.configuration =
$scope.configuration || {}; $scope.configuration || {};
// Make sure there is a "panels" field in the // Make sure there is a "panels" field in the
// view configuration. // view configuration.
$scope.configuration.panels = $scope.configuration.panels =
$scope.configuration.panels || {}; $scope.configuration.panels || {};
// Store the position of this panel.
$scope.configuration.panels[self.activeDragId] = $scope.configuration.panels[self.activeDragId] =
self.rawPositions[self.activeDragId]; $scope.configuration.panels[self.activeDragId] || {};
// Store the position and dimensions of this panel.
$scope.configuration.panels[self.activeDragId].position =
self.rawPositions[self.activeDragId].position;
$scope.configuration.panels[self.activeDragId].dimensions =
self.rawPositions[self.activeDragId].dimensions;
// Mark this object as dirty to encourage persistence // Mark this object as dirty to encourage persistence
if ($scope.commit) { if ($scope.commit) {
$scope.commit("Moved frame."); $scope.commit("Moved frame.");
@@ -144,6 +167,10 @@ define(
// Watch for changes to the grid size in the model // Watch for changes to the grid size in the model
$scope.$watch("model.layoutGrid", updateGridSize); $scope.$watch("model.layoutGrid", updateGridSize);
$scope.$watch("selection", function (selection) {
this.selection = selection;
}.bind(this));
// Update composed objects on screen, and position panes // Update composed objects on screen, and position panes
$scope.$watchCollection("model.composition", refreshComposition); $scope.$watchCollection("model.composition", refreshComposition);
@@ -151,6 +178,20 @@ define(
$scope.$on("mctDrop", handleDrop); $scope.$on("mctDrop", handleDrop);
} }
// Set a default value for hasFrame property on a panel.
// A 'hyperlink' object should have no frame by default.
LayoutController.prototype.setDefaultFrame = function () {
var panels = this.$scope.configuration.panels;
this.$scope.composition.forEach(function (object) {
var id = object.getId();
if (panels[id] && panels[id].hasFrame === undefined) {
panels[id].hasFrame = object.getModel().type === 'hyperlink' ? false : true;
}
});
};
// Convert from { positions: ..., dimensions: ... } to an // Convert from { positions: ..., dimensions: ... } to an
// appropriate ng-style argument, to position frames. // appropriate ng-style argument, to position frames.
LayoutController.prototype.convertPosition = function (raw) { LayoutController.prototype.convertPosition = function (raw) {
@@ -293,9 +334,100 @@ define(
* view configuration. * view configuration.
*/ */
LayoutController.prototype.endDrag = function () { LayoutController.prototype.endDrag = function () {
this.frameMoved = true;
setTimeout(function () {
this.frameMoved = false;
}.bind(this), 0);
this.endDragInScope(); this.endDragInScope();
}; };
/**
* Check if the object is currently selected.
*
* @param {string} obj the object to check for selection
* @returns {boolean} true if selected, otherwise false
*/
LayoutController.prototype.selected = function (obj) {
return this.selectedId && this.selectedId === obj.getId();
};
/**
* Set the active user selection in this view.
*
* @param event the mouse event
* @param {string} id the object id
*/
LayoutController.prototype.select = function (event, id) {
if (event) {
event.stopPropagation();
if (this.selection) {
event.preventDefault();
}
}
var self = this;
var selectedObj = {};
var configuration = this.$scope.configuration;
this.selectedId = id;
// Toggle the visibility of the object frame
function toggle() {
// create new selection object so toolbar updates.
selectedObj = {};
configuration.panels[id].hasFrame =
!configuration.panels[id].hasFrame;
// Change which method is exposed, to influence
// which button is shown in the toolbar
selectedObj[configuration.panels[id].hasFrame ? HIDE : SHOW] = toggle;
self.selection.deselect();
self.selection.select(selectedObj);
}
// Expose initial toggle
selectedObj[configuration.panels[id].hasFrame ? HIDE : SHOW] = toggle;
if (this.selection) {
this.selection.select(selectedObj);
}
};
/**
* Clear the current user selection.
*/
LayoutController.prototype.clearSelection = function (event) {
// Keep the selection if the frame is moved.
if (this.frameMoved) {
this.frameMoved = false;
return;
}
if (this.selection) {
this.selection.deselect();
delete this.selectedId;
}
};
/**
* Check if the object has frame.
*/
LayoutController.prototype.hasFrame = function (obj) {
return this.$scope.configuration.panels[obj.getId()].hasFrame;
};
/**
* Get the size of the grid, in pixels. The returned array
* is in the form `[x, y]`.
* @returns {number[]} the grid size
*/
LayoutController.prototype.getGridSize = function () {
return this.gridSize;
};
return LayoutController; return LayoutController;
} }
); );

View File

@@ -27,7 +27,6 @@ define([
) { ) {
var OVERLAY_TEMPLATE = '' + var OVERLAY_TEMPLATE = '' +
'<div class="abs overlay l-large-view">' +
' <div class="abs blocker"></div>' + ' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' + ' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' + ' <a class="close icon-x-in-circle"></a>' +
@@ -37,8 +36,7 @@ define([
' <a class="t-done s-button major">Done</a>' + ' <a class="t-done s-button major">Done</a>' +
' </div>' + ' </div>' +
' </div>' + ' </div>' +
' </div>' + ' </div>';
'</div>';
/** /**
* MCT Trigger Modal is intended for use in only one location: inside the * MCT Trigger Modal is intended for use in only one location: inside the
@@ -81,7 +79,8 @@ define([
function openOverlay() { function openOverlay() {
// Remove frame classes from being applied in a non-frame context // Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template'); $(frame).removeClass('frame frame-template');
overlay = document.createElement('span'); overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE; overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents'); overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close'); closeButton = overlay.querySelector('a.close');

View File

@@ -53,6 +53,17 @@ define(
}; };
} }
// Utility function to find a watch for a given expression
function findWatch(expr) {
var watch;
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
watch = call.args[1];
}
});
return watch;
}
beforeEach(function () { beforeEach(function () {
mockScope = jasmine.createSpyObj( mockScope = jasmine.createSpyObj(
"$scope", "$scope",
@@ -60,7 +71,7 @@ define(
); );
mockEvent = jasmine.createSpyObj( mockEvent = jasmine.createSpyObj(
'event', 'event',
['preventDefault'] ['preventDefault', 'stopPropagation']
); );
testModel = {}; testModel = {};
@@ -72,7 +83,8 @@ define(
panels: { panels: {
a: { a: {
position: [20, 10], position: [20, 10],
dimensions: [5, 5] dimensions: [5, 5],
hasFrame: true
} }
} }
}; };
@@ -82,10 +94,17 @@ define(
mockScope.domainObject = mockDomainObject("mockDomainObject"); mockScope.domainObject = mockDomainObject("mockDomainObject");
mockScope.model = testModel; mockScope.model = testModel;
mockScope.configuration = testConfiguration; mockScope.configuration = testConfiguration;
mockScope.selection = jasmine.createSpyObj(
'selection',
['select', 'get', 'selected', 'deselect']
);
spyOn(mockScope.domainObject, "useCapability").andCallThrough(); spyOn(mockScope.domainObject, "useCapability").andCallThrough();
controller = new LayoutController(mockScope); controller = new LayoutController(mockScope);
spyOn(controller, "layoutPanels").andCallThrough(); spyOn(controller, "layoutPanels").andCallThrough();
findWatch("selection")(mockScope.selection);
}); });
// Model changes will indicate that panel positions // Model changes will indicate that panel positions
@@ -289,6 +308,43 @@ define(
expect(controller.getFrameStyle("b")) expect(controller.getFrameStyle("b"))
.not.toEqual(oldStyle); .not.toEqual(oldStyle);
}); });
it("allows panels to be selected", function () {
var childObj = mockCompositionObjects[0];
controller.select(mockEvent, childObj.getId());
expect(mockEvent.stopPropagation).toHaveBeenCalled();
expect(controller.selected(childObj)).toBe(true);
});
it("allows selection to be cleared", function () {
var childObj = mockCompositionObjects[0];
controller.select(null, childObj.getId());
controller.clearSelection();
expect(controller.selected(childObj)).toBeFalsy();
});
it("does not clear selection when moving/resizing", function () {
mockScope.$watchCollection.mostRecentCall.args[1]();
var childObj = mockCompositionObjects[0];
var id = childObj.getId();
controller.select(mockEvent, id);
// Do a drag
controller.startDrag(id, [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
controller.clearSelection();
expect(controller.selected(childObj)).toBe(true);
});
}); });
} }
); );