GUI -- Added user feedback "flash" message function.
- flash hover mode when H key pressed. - additional cleanup. Change-Id: I14dd47c41842c3f6cb68d2c9fbe3ee96ad23ae86
Showing
8 changed files
with
227 additions
and
105 deletions
web/gui/src/main/webapp/feedback.css
0 → 100644
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 | +} |
web/gui/src/main/webapp/feedback.js
0 → 100644
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') | ... | ... |
-
Please register or login to post a comment