Simon Hunt

GUI -- Added user feedback "flash" message function.

- flash hover mode when H key pressed.
- additional cleanup.

Change-Id: I14dd47c41842c3f6cb68d2c9fbe3ee96ad23ae86
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Feedback layer -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +#feedback svg {
24 + position: absolute;
25 + bottom: 0;
26 + opacity: 0.5;
27 +}
28 +
29 +#feedback svg g.feedbackItem {
30 + background-color: teal;
31 +}
32 +
33 +#feedback svg g.feedbackItem rect {
34 + fill: #888;
35 + stroke: #666;
36 + stroke-width: 3;
37 + opacity: 0.5
38 +}
39 +
40 +#feedback svg g.feedbackItem text {
41 + fill: #000;
42 + stroke: none;
43 + text-anchor: middle;
44 + alignment-baseline: middle;
45 +
46 + font-size: 16pt;
47 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Feedback layer
19 +
20 + Defines the feedback layer for the UI. Used to give user visual feedback
21 + of choices, typically responding to keystrokes.
22 +
23 + @author Simon Hunt
24 + */
25 +
26 +(function (onos){
27 + 'use strict';
28 +
29 + // API's
30 + var api = onos.api;
31 +
32 + // Config variables
33 + var w = '100%',
34 + h = 200,
35 + fade = 750,
36 + showFor = 2000,
37 + vb = '-200 -' + (h/2) + ' 400 ' + h,
38 + xpad = 20,
39 + ypad = 10;
40 +
41 + // State variables
42 + var timer = null,
43 + data = [];
44 +
45 + // DOM elements and the like
46 + var fb = d3.select('#feedback');
47 +
48 + var svg = fb.append('svg').attr({
49 + width: w,
50 + height: h,
51 + viewBox: vb
52 + });
53 +
54 + function computeBox(el) {
55 + var text = el.select('text'),
56 + box = text.node().getBBox();
57 +
58 + // center
59 + box.x = -box.width / 2;
60 + box.y = -box.height / 2;
61 +
62 + // add some padding
63 + box.x -= xpad;
64 + box.width += xpad * 2;
65 + box.y -= ypad;
66 + box.height += ypad * 2;
67 +
68 + return box;
69 + }
70 +
71 + function updateFeedback() {
72 + var items = svg.selectAll('.feedbackItem')
73 + .data(data);
74 +
75 + var entering = items.enter()
76 + .append('g')
77 + .attr({
78 + class: 'feedbackItem',
79 + opacity: 0
80 + })
81 + .transition()
82 + .duration(fade)
83 + .attr('opacity', 1);
84 +
85 + entering.each(function (d) {
86 + var el = d3.select(this),
87 + box;
88 +
89 + d.el = el;
90 + el.append('rect').attr({ rx: 10, ry: 10});
91 + el.append('text').text(d.label);
92 + box = computeBox(el);
93 + el.select('rect').attr(box);
94 + });
95 +
96 + items.exit()
97 + .transition()
98 + .duration(fade)
99 + .attr({ opacity: 0})
100 + .remove();
101 + }
102 +
103 + function clearFlash() {
104 + if (timer) {
105 + clearInterval(timer);
106 + }
107 + data = [];
108 + updateFeedback();
109 + }
110 +
111 + // for now, simply display some text feedback
112 + function flash(text) {
113 + // cancel old scheduled event if there was one
114 + if (timer) {
115 + clearInterval(timer);
116 + }
117 + timer = setInterval(function () {
118 + clearFlash();
119 + }, showFor);
120 +
121 + data = [{
122 + label: text
123 + }];
124 + updateFeedback();
125 + }
126 +
127 + onos.ui.addLib('feedback', {
128 + flash: flash
129 + });
130 +}(ONOS));
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
42 <link rel="stylesheet" href="onos2.css"> 42 <link rel="stylesheet" href="onos2.css">
43 <link rel="stylesheet" href="mast2.css"> 43 <link rel="stylesheet" href="mast2.css">
44 <link rel="stylesheet" href="floatPanel.css"> 44 <link rel="stylesheet" href="floatPanel.css">
45 + <link rel="stylesheet" href="feedback.css">
45 46
46 <!-- This is where contributed stylesheets get INJECTED --> 47 <!-- This is where contributed stylesheets get INJECTED -->
47 <!-- TODO: replace with template marker and inject refs server-side --> 48 <!-- TODO: replace with template marker and inject refs server-side -->
...@@ -71,6 +72,9 @@ ...@@ -71,6 +72,9 @@
71 <div id="alerts"> 72 <div id="alerts">
72 <!-- NOTE: alert content injected here, as needed --> 73 <!-- NOTE: alert content injected here, as needed -->
73 </div> 74 </div>
75 + <div id="feedback">
76 + <!-- NOTE: feedback to user -->
77 + </div>
74 </div> 78 </div>
75 79
76 <!-- Initialize the UI...--> 80 <!-- Initialize the UI...-->
...@@ -89,6 +93,7 @@ ...@@ -89,6 +93,7 @@
89 93
90 <!-- Framework module files included here --> 94 <!-- Framework module files included here -->
91 <script src="mast2.js"></script> 95 <script src="mast2.js"></script>
96 + <script src="feedback.js"></script>
92 97
93 <!-- View Module Templates; REMOVE THESE LINES, FOR PRODUCTION --> 98 <!-- View Module Templates; REMOVE THESE LINES, FOR PRODUCTION -->
94 <script src="module-svg-template.js"></script> 99 <script src="module-svg-template.js"></script>
......
...@@ -64,6 +64,8 @@ ...@@ -64,6 +64,8 @@
64 font-size: 10pt; 64 font-size: 10pt;
65 margin: 4px 2px; 65 margin: 4px 2px;
66 padding: 1px 6px; 66 padding: 1px 6px;
67 + -moz-border-radius: 3px;
68 + border-radius: 3px;
67 cursor: pointer; 69 cursor: pointer;
68 } 70 }
69 71
...@@ -102,6 +104,8 @@ ...@@ -102,6 +104,8 @@
102 #bb .btn { 104 #bb .btn {
103 margin: 0 4px; 105 margin: 0 4px;
104 padding: 2px 6px; 106 padding: 2px 6px;
107 + -moz-border-radius: 3px;
108 + border-radius: 3px;
105 font-size: 9pt; 109 font-size: 9pt;
106 cursor: pointer; 110 cursor: pointer;
107 } 111 }
......
...@@ -24,6 +24,19 @@ html, body { ...@@ -24,6 +24,19 @@ html, body {
24 height: 100%; 24 height: 100%;
25 } 25 }
26 26
27 +/* This is to ensure that the body does not expand to account for the
28 + flyout details pane, that is positioned "off screen".
29 + */
30 +body {
31 + overflow: hidden;
32 +}
33 +
34 +#frame {
35 + width: 100%;
36 + height: 100%;
37 + background-color: #fff;
38 +}
39 +
27 div.onosView { 40 div.onosView {
28 display: none; 41 display: none;
29 } 42 }
...@@ -32,6 +45,7 @@ div.onosView.currentView { ...@@ -32,6 +45,7 @@ div.onosView.currentView {
32 display: block; 45 display: block;
33 } 46 }
34 47
48 +
35 #alerts { 49 #alerts {
36 display: none; 50 display: none;
37 position: absolute; 51 position: absolute;
...@@ -42,6 +56,8 @@ div.onosView.currentView { ...@@ -42,6 +56,8 @@ div.onosView.currentView {
42 top: 80px; 56 top: 80px;
43 left: 40px; 57 left: 40px;
44 padding: 3px 6px; 58 padding: 3px 6px;
59 + -moz-border-radius: 6px;
60 + border-radius: 6px;
45 box-shadow: 4px 6px 12px #777; 61 box-shadow: 4px 6px 12px #777;
46 } 62 }
47 63
...@@ -67,90 +83,6 @@ div.onosView.currentView { ...@@ -67,90 +83,6 @@ div.onosView.currentView {
67 color: #66d; 83 color: #66d;
68 } 84 }
69 85
70 -/*
71 - * ==============================================================
72 - * END OF NEW ONOS.JS file
73 - * ==============================================================
74 - */
75 -
76 -/*
77 - * === DEBUGGING ======
78 - */
79 -svg {
80 - /*border: 1px dashed red;*/
81 -}
82 -
83 -svg #bg {
84 - opacity: 0.5;
85 -}
86 -
87 -
88 -/*
89 - * Network Graph elements ======================================
90 - */
91 -
92 -
93 -svg g.portLayer rect.port {
94 - fill: #ccc;
95 -}
96 -
97 -svg g.portLayer text {
98 - font: 8pt sans-serif;
99 - pointer-events: none;
100 -}
101 -
102 -
103 -
104 -/* for debugging */
105 -svg .node circle.debug {
106 - fill: white;
107 - stroke: red;
108 -}
109 -svg .node rect.debug {
110 - fill: yellow;
111 - stroke: red;
112 - opacity: 0.35;
113 -}
114 -
115 -
116 -
117 -svg .link.inactive,
118 -svg .port.inactive,
119 -svg .portText.inactive,
120 -svg .node.inactive rect,
121 -svg .node.inactive circle,
122 -svg .node.inactive text,
123 -svg .node.inactive image {
124 - opacity: .1;
125 -}
126 -
127 -svg .node.inactive.selected rect,
128 -svg .node.inactive.selected text,
129 -svg .node.inactive.selected image {
130 - opacity: .6;
131 -}
132 -
133 -/*
134 - * =============================================================
135 - */
136 -
137 -/*
138 - * Specific structural elements
139 - */
140 -
141 -/* This is to ensure that the body does not expand to account for the
142 - flyout details pane, that is positioned "off screen".
143 - */
144 -body {
145 - overflow: hidden;
146 -}
147 -
148 -
149 -#frame {
150 - width: 100%;
151 - height: 100%;
152 - background-color: #fff;
153 -}
154 86
155 #flyout { 87 #flyout {
156 position: absolute; 88 position: absolute;
...@@ -200,4 +132,3 @@ body { ...@@ -200,4 +132,3 @@ body {
200 background-color: #ccc; 132 background-color: #ccc;
201 border: 0; 133 border: 0;
202 } 134 }
203 -
......
...@@ -588,6 +588,7 @@ ...@@ -588,6 +588,7 @@
588 setKeys: this.setKeys, 588 setKeys: this.setKeys,
589 dataLoadError: this.dataLoadError, 589 dataLoadError: this.dataLoadError,
590 alert: this.alert, 590 alert: this.alert,
591 + flash: this.flash,
591 theme: this.theme 592 theme: this.theme
592 } 593 }
593 }, 594 },
...@@ -697,6 +698,10 @@ ...@@ -697,6 +698,10 @@
697 doAlert(msg); 698 doAlert(msg);
698 }, 699 },
699 700
701 + flash: function (msg) {
702 + libApi.feedback.flash(msg);
703 + },
704 +
700 dataLoadError: function (err, url) { 705 dataLoadError: function (err, url) {
701 var msg = 'Data Load Error\n\n' + 706 var msg = 'Data Load Error\n\n' +
702 err.status + ' -- ' + err.statusText + '\n\n' + 707 err.status + ' -- ' + err.statusText + '\n\n' +
......
...@@ -188,8 +188,13 @@ svg .node.host circle { ...@@ -188,8 +188,13 @@ svg .node.host circle {
188 188
189 #topo-detail h2 { 189 #topo-detail h2 {
190 position: absolute; 190 position: absolute;
191 - /*display: inline-block;*/ 191 + margin: 0px 4px;
192 - /*vertical-align: bottom;*/ 192 + top: 20px;
193 + left: 50px;
194 + color: black;
195 +}
196 +
197 +#topo-detail h3 {
193 margin: 0px 4px; 198 margin: 0px 4px;
194 top: 20px; 199 top: 20px;
195 left: 50px; 200 left: 50px;
......
...@@ -233,7 +233,8 @@ ...@@ -233,7 +233,8 @@
233 // Key Callbacks 233 // Key Callbacks
234 234
235 function testMe(view) { 235 function testMe(view) {
236 - view.alert('Theme is ' + view.theme()); 236 + //view.alert('Theme is ' + view.theme());
237 + //view.flash('This is some text');
237 } 238 }
238 239
239 function abortIfLive() { 240 function abortIfLive() {
...@@ -325,7 +326,7 @@ ...@@ -325,7 +326,7 @@
325 if (hoverMode === hoverModes.length) { 326 if (hoverMode === hoverModes.length) {
326 hoverMode = 0; 327 hoverMode = 0;
327 } 328 }
328 - console.log('Hover Mode:' + hoverMode + ': ' + hoverModes[hoverMode]); 329 + view.flash('Hover Mode: ' + hoverModes[hoverMode]);
329 } 330 }
330 331
331 function togglePorts(view) { 332 function togglePorts(view) {
...@@ -1504,8 +1505,8 @@ ...@@ -1504,8 +1505,8 @@
1504 1505
1505 node.append('rect') 1506 node.append('rect')
1506 .attr({ 1507 .attr({
1507 - 'rx': 5, 1508 + rx: 5,
1508 - 'ry': 5 1509 + ry: 5
1509 }); 1510 });
1510 1511
1511 node.append('text') 1512 node.append('text')
...@@ -1513,12 +1514,8 @@ ...@@ -1513,12 +1514,8 @@
1513 .attr('dy', '1.1em'); 1514 .attr('dy', '1.1em');
1514 1515
1515 box = adjustRectToFitText(node); 1516 box = adjustRectToFitText(node);
1516 - 1517 + node.select('rect').attr(box);
1517 - node.select('rect')
1518 - .attr(box);
1519 -
1520 addDeviceIcon(node, box, noLabel, iconGlyphUrl(d)); 1518 addDeviceIcon(node, box, noLabel, iconGlyphUrl(d));
1521 -
1522 }); 1519 });
1523 1520
1524 // TODO: better place for this configuration state 1521 // TODO: better place for this configuration state
...@@ -1930,9 +1927,9 @@ ...@@ -1930,9 +1927,9 @@
1930 function populateMultiSelect() { 1927 function populateMultiSelect() {
1931 detailPane.empty(); 1928 detailPane.empty();
1932 1929
1933 - var title = detailPane.append("h2"), 1930 + var title = detailPane.append('h3'),
1934 - table = detailPane.append("table"), 1931 + table = detailPane.append('table'),
1935 - tbody = table.append("tbody"); 1932 + tbody = table.append('tbody');
1936 1933
1937 title.text('Selected Nodes'); 1934 title.text('Selected Nodes');
1938 1935
...@@ -1949,9 +1946,9 @@ ...@@ -1949,9 +1946,9 @@
1949 var svg = detailPane.append('svg'), 1946 var svg = detailPane.append('svg'),
1950 iid = iconGlyphUrl(data); 1947 iid = iconGlyphUrl(data);
1951 1948
1952 - var title = detailPane.append("h2"), 1949 + var title = detailPane.append('h2'),
1953 - table = detailPane.append("table"), 1950 + table = detailPane.append('table'),
1954 - tbody = table.append("tbody"); 1951 + tbody = table.append('tbody');
1955 1952
1956 appendGlyph(svg, 0, 0, 40, iid); 1953 appendGlyph(svg, 0, 0, 40, iid);
1957 title.text(data.id); 1954 title.text(data.id);
...@@ -2063,9 +2060,7 @@ ...@@ -2063,9 +2060,7 @@
2063 // TODO: toggle button (and other widgets in the masthead) should be provided 2060 // TODO: toggle button (and other widgets in the masthead) should be provided
2064 // by the framework; not generated by the view. 2061 // by the framework; not generated by the view.
2065 2062
2066 - var showInstances, 2063 + var showInstances;
2067 - doPanZoom,
2068 - showTrafficOnHover;
2069 2064
2070 function addButtonBar(view) { 2065 function addButtonBar(view) {
2071 var bb = d3.select('#mast') 2066 var bb = d3.select('#mast')
......