1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.vaadin.gwt.client.magnoliashell.shell;
35
36 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryCallback;
37 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryWrapper;
38 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.ShellState;
39 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.ShellMessageWidget;
40 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.ShellMessageWidget.MessageType;
41 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VInfoMessage;
42 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VShellErrorMessage;
43 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VWarningMessage;
44 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.animation.JQueryAnimation;
45 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.AppsViewportWidget;
46 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.ViewportWidget;
47 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.Fragment;
48 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ShellAppType;
49 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ViewportType;
50
51 import java.util.EnumMap;
52 import java.util.Map;
53
54 import com.google.gwt.core.client.Scheduler;
55 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
56 import com.google.gwt.event.dom.client.KeyCodes;
57 import com.google.gwt.event.dom.client.KeyPressEvent;
58 import com.google.gwt.event.dom.client.KeyPressHandler;
59 import com.google.gwt.regexp.shared.RegExp;
60 import com.google.gwt.user.client.DOM;
61 import com.google.gwt.user.client.Element;
62 import com.google.gwt.user.client.Window;
63 import com.google.gwt.user.client.ui.FocusPanel;
64 import com.google.gwt.user.client.ui.RootPanel;
65 import com.google.gwt.user.client.ui.Widget;
66 import com.googlecode.mgwt.ui.client.widget.touch.TouchPanel;
67
68
69
70
71 public class MagnoliaShellViewImpl extends TouchPanel implements MagnoliaShellView {
72
73 public static final String CLASS_NAME = "v-magnolia-shell";
74
75 public static final String VIEWPORT_SLOT_CLASS_NAME = "v-shell-viewport-slot";
76
77 private final Map<ViewportType, ViewportWidget> viewports = new EnumMap<ViewportType, ViewportWidget>(ViewportType.class);
78
79 private final ShellAppLauncher mainAppLauncher;
80
81 private ShellMessageWidget lowPriorityMessage;
82
83 private ShellMessageWidget hiPriorityMessage;
84
85 private Presenter presenter;
86
87 private JQueryAnimation viewportShifter = new JQueryAnimation();
88
89 private final Element viewportSlot = DOM.createDiv();
90
91
92
93 private static final RegExp GROOVY_CONSOLE_URL_FRAGMENT_REGEX = RegExp.compile("(app:groovy:)(console|;)");
94
95
96 private final FocusPanel blurHelper = new FocusPanel();
97
98 public MagnoliaShellViewImpl() {
99 super();
100 this.mainAppLauncher = new ShellAppLauncher();
101 getElement().setClassName(CLASS_NAME);
102 viewportSlot.setClassName(VIEWPORT_SLOT_CLASS_NAME);
103 add(mainAppLauncher, getElement());
104
105
106 getElement().appendChild(viewportSlot);
107 viewportShifter.addCallback(new JQueryCallback() {
108 @Override
109 public void execute(JQueryWrapper query) {
110 presenter.updateViewportLayout(appViewport());
111 }
112 });
113
114 initKeyboardShortcutSupport();
115 }
116
117
118
119
120
121
122 protected void initKeyboardShortcutSupport() {
123
124 add(blurHelper, getElement());
125
126 KeyPressHandler keyboardShortcutHandler = new KeyPressHandler() {
127
128 @Override
129 public void onKeyPress(KeyPressEvent event) {
130
131
132 if (isFocusedElementAnInputField()) {
133 return;
134 }
135
136
137 if (event.isAnyModifierKeyDown()) {
138 return;
139 }
140
141 if (isAppInFocusGroovyConsole()) {
142 return;
143 }
144
145 char c = event.getCharCode();
146
147 switch (c) {
148
149
150 case '1':
151 mainAppLauncher.toggleShellApp(ShellAppType.APPLAUNCHER);
152 break;
153 case '2':
154 mainAppLauncher.toggleShellApp(ShellAppType.PULSE);
155 break;
156 case '3':
157 mainAppLauncher.toggleShellApp(ShellAppType.FAVORITE);
158 break;
159
160
161 case '9':
162
163 if (appViewport().readyForAppSwipeOrShortcutNavigation()) {
164 ShellState.get().setAppStarting();
165 appViewport().goToPreviousApp();
166 }
167 break;
168 case '0':
169
170 if (appViewport().readyForAppSwipeOrShortcutNavigation()) {
171 ShellState.get().setAppStarting();
172 appViewport().goToNextApp();
173 }
174 break;
175
176 default:
177
178 }
179 }
180 };
181 RootPanel.get().addDomHandler(keyboardShortcutHandler, KeyPressEvent.getType());
182
183
184
185
186
187
188 KeyPressHandler escapeKeyPressHandler = new KeyPressHandler() {
189 @Override
190 public void onKeyPress(KeyPressEvent event) {
191 int code = event.getNativeEvent().getKeyCode();
192 if (code == KeyCodes.KEY_ESCAPE) {
193 blurHelper.getElement().focus();
194 }
195 }
196 };
197 RootPanel.get().addDomHandler(escapeKeyPressHandler, KeyPressEvent.getType());
198 }
199
200
201
202
203 protected boolean isFocusedElementAnInputField() {
204 Element focused = elementInFocus(RootPanel.get().getElement());
205 String tagName = focused.getTagName();
206
207 if ("input".equalsIgnoreCase(tagName) || "select".equalsIgnoreCase(tagName) || "textarea".equalsIgnoreCase(tagName)) {
208 return true;
209 }
210 return false;
211 }
212
213 protected native Element elementInFocus(Element element)
214
215 ;
216
217
218 protected AppsViewportWidget appViewport() {
219 return (AppsViewportWidget) viewports.get(ViewportType.APP);
220 }
221
222 protected void replaceWidget(final Widget oldWidget, final Widget newWidget) {
223 if (oldWidget != newWidget) {
224 if (oldWidget != null) {
225 remove(oldWidget);
226 }
227 }
228 if (getWidgetIndex(newWidget) < 0) {
229 add(newWidget, viewportSlot);
230 }
231 }
232
233 @Override
234 public void setPresenter(Presenter presenter) {
235 this.presenter = presenter;
236 mainAppLauncher.setListener(presenter);
237 }
238
239 @Override
240 public void showMessage(MessageType type, String topic, String message, String id) {
241 final ShellMessageWidget msg;
242 switch (type) {
243 case WARNING:
244 msg = new VWarningMessage(this, topic, message, id);
245 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
246 lowPriorityMessage.hide();
247 }
248 lowPriorityMessage = msg;
249 break;
250 case INFO:
251 msg = new VInfoMessage(this, topic, message, id);
252 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
253 lowPriorityMessage.hide();
254 }
255 lowPriorityMessage = msg;
256 break;
257 case ERROR:
258 msg = new VShellErrorMessage(this, topic, message, id);
259 if (hiPriorityMessage != null && getWidgetIndex(hiPriorityMessage) != -1) {
260 hiPriorityMessage.hide();
261 }
262 hiPriorityMessage = msg;
263 break;
264 default:
265 msg = null;
266 }
267 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
268
269 @Override
270 public void execute() {
271 if (msg != null) {
272 add(msg, getElement());
273 }
274 }
275 });
276 }
277
278 @Override
279 public void hideAllMessages() {
280 if (hiPriorityMessage != null && getWidgetIndex(hiPriorityMessage) != -1) {
281 hiPriorityMessage.hideWithoutTransition();
282 }
283 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
284 lowPriorityMessage.hideWithoutTransition();
285 }
286
287 hiPriorityMessage = null;
288 lowPriorityMessage = null;
289 }
290
291 @Override
292 public void updateViewport(ViewportWidget viewport, ViewportType type) {
293 ViewportWidget oldViewport = viewports.get(type);
294 if (oldViewport != viewport) {
295 replaceWidget(oldViewport, viewport);
296 viewports.put(type, viewport);
297 }
298 }
299
300 @Override
301 public void shiftViewportsVertically(int shiftPx, boolean animated) {
302 viewportShifter.setProperty("top", mainAppLauncher.getOffsetHeight() + shiftPx);
303 if (shiftPx == 0 || shiftPx == 60) {
304 viewportShifter.clearTopAfterThisAnimation();
305 }
306 viewportShifter.run(animated ? 300 : 0, viewportSlot);
307 }
308
309 @Override
310 public void setShellAppIndication(ShellAppType type, int indication) {
311 mainAppLauncher.setIndication(type, indication);
312 }
313
314 @Override
315 public void closeMessageEager(final String id) {
316 presenter.removeMessage(id);
317 }
318
319 @Override
320 public void navigateToMessageDetails(String id) {
321 presenter.activateShellApp(Fragment.fromString("shell:pulse:messages/" + id));
322 }
323
324 @Override
325 public void updateShellDivet() {
326 mainAppLauncher.updateDivet();
327 }
328
329 @Override
330 public void openOverlayOnWidget(Widget overlayWidget, Widget overlayParent) {
331 add(overlayWidget, overlayParent.getElement());
332 }
333
334 @Override
335 public void onShellAppStarting(ShellAppType type) {
336 mainAppLauncher.activateControl(type);
337 }
338
339 @Override
340 public void onAppStarting() {
341 mainAppLauncher.deactivateControls();
342 }
343
344 @Override
345 public void setUserMenu(Widget widget) {
346 mainAppLauncher.setUserMenu(widget);
347 }
348
349 @Override
350 public void onLoad() {
351 super.onLoad();
352 presenter.initHistory();
353 }
354
355 @Override
356 public boolean hasOverlay(Widget widget) {
357 return getWidgetIndex(widget) != -1;
358 }
359
360
361
362
363 private boolean isAppInFocusGroovyConsole() {
364 String hash = Window.Location.getHash();
365 return GROOVY_CONSOLE_URL_FRAGMENT_REGEX.test(hash);
366 }
367
368 }