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.viewport.widget;
35
36 import info.magnolia.ui.vaadin.gwt.client.CloseButton;
37 import info.magnolia.ui.vaadin.gwt.client.FullScreenButton;
38 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryCallback;
39 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryWrapper;
40 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.AppsTransitionDelegate;
41 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.MagnoliaSwipeRecognizer;
42 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.animation.FadeAnimation;
43 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.animation.SlideAnimation;
44 import info.magnolia.ui.vaadin.gwt.client.tabsheet.connector.MagnoliaTabSheetConnector;
45
46 import java.util.Iterator;
47
48 import com.google.gwt.dom.client.Style.Visibility;
49 import com.google.gwt.event.shared.HandlerRegistration;
50 import com.google.gwt.user.client.DOM;
51 import com.google.gwt.user.client.Element;
52 import com.google.gwt.user.client.Event;
53 import com.google.gwt.user.client.ui.RootPanel;
54 import com.google.gwt.user.client.ui.Widget;
55 import com.googlecode.mgwt.dom.client.event.touch.TouchCancelEvent;
56 import com.googlecode.mgwt.dom.client.event.touch.TouchCancelHandler;
57 import com.googlecode.mgwt.dom.client.event.touch.TouchEndEvent;
58 import com.googlecode.mgwt.dom.client.event.touch.TouchEndHandler;
59 import com.googlecode.mgwt.dom.client.recognizer.swipe.HasSwipeHandlers;
60 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeEndEvent;
61 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeEndHandler;
62 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeEvent;
63 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeEvent.DIRECTION;
64 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeMoveEvent;
65 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeMoveHandler;
66 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeStartEvent;
67 import com.googlecode.mgwt.dom.client.recognizer.swipe.SwipeStartHandler;
68 import com.googlecode.mgwt.ui.client.widget.touch.TouchDelegate;
69 import com.vaadin.client.Util;
70
71
72
73
74 public class AppsViewportWidget extends ViewportWidget implements HasSwipeHandlers {
75
76 public static final String APP_INACTIVE_CLASS_NAME = "app-inactive";
77
78
79
80
81 public interface Listener {
82 void closeCurrentApp();
83
84 void setCurrentApp(String name);
85 };
86
87 private static final int SWIPE_OUT_THRESHOLD = 300;
88
89 private final AppPreloader preloader = new AppPreloader();
90
91 private boolean isAppClosing = false;
92
93 private boolean isCurtainVisible = false;
94
95 private Listener listener;
96
97 private final TouchDelegate delegate = new TouchDelegate(this);
98
99 private final FullScreenButton fullScreenButton = new FullScreenButton();
100
101 private Element curtain = DOM.createDiv();
102
103 private void closeCurrentApp() {
104 if (!isAppClosing()) {
105 isAppClosing = true;
106 listener.closeCurrentApp();
107 }
108 }
109
110 private void toggleFullScreen() {
111 String cssClasses = RootPanel.get().getStyleName();
112 boolean isFullScreen = cssClasses.contains("fullscreen");
113
114 setFullScreen(!isFullScreen);
115 }
116
117 private CloseButton closeButton = new CloseButton();
118
119
120
121
122 public void setFullScreen(boolean isFullScreen) {
123
124 if (isFullScreen) {
125
126 RootPanel.get().addStyleName("fullscreen");
127
128 fullScreenButton.getElement().addClassName("icon-extend-header");
129 fullScreenButton.getElement().removeClassName("icon-collapse-header");
130 } else {
131
132
133 RootPanel.get().removeStyleName("fullscreen");
134
135 fullScreenButton.getElement().addClassName("icon-collapse-header");
136 fullScreenButton.getElement().removeClassName("icon-extend-header");
137 }
138 }
139
140 public AppsViewportWidget(final Listener listener) {
141 super();
142 this.listener = listener;
143 DOM.sinkEvents(getElement(), Event.ONCLICK);
144 curtain.setClassName("v-curtain v-curtain-green");
145 closeButton.addStyleDependentName("app");
146 delegate.addTouchEndHandler(new TouchEndHandler() {
147 @Override
148 public void onTouchEnd(TouchEndEvent event) {
149 Element target = event.getNativeEvent().getEventTarget().cast();
150 if (closeButton.getElement().isOrHasChild(target)) {
151 closeCurrentApp();
152 } else if (fullScreenButton.getElement().isOrHasChild(target)) {
153 toggleFullScreen();
154 }
155 }
156 });
157
158 bindTouchHandlers();
159
160 }
161
162 public void goToNextApp() {
163 if (getWidgetCount() > 1) {
164 processSwipe(-1);
165 switchToApp(DIRECTION.RIGHT_TO_LEFT);
166 }
167 }
168
169 public void goToPreviousApp() {
170 if (getWidgetCount() > 1) {
171 processSwipe(1);
172 switchToApp(DIRECTION.LEFT_TO_RIGHT);
173 }
174 }
175
176
177
178
179
180 public Widget getCurrentApp(){
181 if (getWidgetCount() < 1) {
182 return null;
183 }else{
184 for (int w=0; w < getWidgetCount(); w++){
185 Widget app = getWidget(w);
186 String style = app.getStyleName();
187 if (!app.getStyleName().contains("app-inactive")){
188 return app;
189 }
190 }
191 }
192
193 return null;
194 }
195
196 public Element getCurtain() {
197 return curtain;
198 }
199
200 public void setCurtainVisible(boolean visible) {
201 if (isCurtainVisible != visible) {
202 this.isCurtainVisible = visible;
203 ((AppsTransitionDelegate) getTransitionDelegate()).setCurtainVisible(isCurtainVisible);
204 }
205 }
206
207
208 @Override
209 public void showChildNoTransition(Widget w) {
210 getElement().appendChild(closeButton.getElement());
211 getElement().appendChild(fullScreenButton.getElement());
212 Widget formerVisible = getVisibleChild();
213
214 if (formerVisible != null && !isAppClosing()) {
215 formerVisible.getElement().getStyle().setVisibility(Visibility.HIDDEN);
216 formerVisible.addStyleName(APP_INACTIVE_CLASS_NAME);
217 }
218 w.setVisible(true);
219 w.removeStyleName(APP_INACTIVE_CLASS_NAME);
220 w.getElement().getStyle().clearVisibility();
221 }
222
223 @Override
224 public void removeChild(Widget w) {
225 ((AppsTransitionDelegate) getTransitionDelegate()).removeWidget(w);
226 getElement().removeChild(closeButton.getElement());
227 getElement().removeChild(fullScreenButton.getElement());
228 }
229
230 @Override
231 public void removeChildNoTransition(Widget w) {
232 super.removeChildNoTransition(w);
233 isAppClosing = false;
234 }
235
236 public boolean isAppClosing() {
237 return isAppClosing;
238 }
239
240
241 public void showAppPreloader(final String appName) {
242 preloader.setCaption(appName);
243 preloader.addStyleName("zoom-in");
244 RootPanel.get().add(preloader);
245 }
246
247 public boolean hasPreloader() {
248 return RootPanel.get().getWidgetIndex(preloader) >= 0;
249 }
250
251 public void removePreloader() {
252 final FadeAnimation preloaderFadeOut = new FadeAnimation(0d, true);
253 preloaderFadeOut.addCallback(new JQueryCallback() {
254 @Override
255 public void execute(JQueryWrapper query) {
256 RootPanel.get().remove(preloader);
257 }
258 });
259 preloaderFadeOut.run(500, preloader.getElement());
260 }
261
262
263 private void bindTouchHandlers() {
264 DOM.sinkEvents(getElement(), Event.TOUCHEVENTS);
265 delegate.addTouchHandler(new MagnoliaSwipeRecognizer(delegate, SWIPE_OUT_THRESHOLD));
266 addSwipeStartHandler(new SwipeStartHandler() {
267 @Override
268 public void onSwipeStart(SwipeStartEvent event) {
269 processSwipe(event.getDistance() * (event.getDirection() == DIRECTION.LEFT_TO_RIGHT ? 1 : -1));
270 }
271 });
272
273 addSwipeMoveHandler(new SwipeMoveHandler() {
274 @Override
275 public void onSwipeMove(SwipeMoveEvent event) {
276 processSwipe(event.getDistance() * (event.getDirection() == SwipeEvent.DIRECTION.LEFT_TO_RIGHT ? 1 : -1));
277 }
278 });
279
280 addSwipeEndHandler(new SwipeEndHandler() {
281 @Override
282 public void onSwipeEnd(SwipeEndEvent event) {
283 if (getWidgetCount() > 1) {
284 final SwipeEvent.DIRECTION direction = event.getDirection();
285 if (event.isDistanceReached()) {
286 switchToApp(direction);
287 } else {
288 final Widget visibleChild = getVisibleChild();
289 final SlideAnimation slideAnimation = new SlideAnimation(false, true);
290 slideAnimation.setTargetValue(0);
291 slideAnimation.run(500, visibleChild.getElement());
292 slideAnimation.addCallback(new JQueryCallback() {
293 @Override
294 public void execute(JQueryWrapper query) {
295
296 dropZIndeces();
297 Widget next = getNextWidget();
298 Widget previous = getPreviousWidget();
299 if (next != null) {
300 next.getElement().getStyle().setVisibility(Visibility.HIDDEN);
301 }
302
303 if (previous != null) {
304 previous.getElement().getStyle().setVisibility(Visibility.HIDDEN);
305 }
306 }
307 });
308 }
309 }
310 }
311 });
312
313 delegate.addTouchCancelHandler(new TouchCancelHandler() {
314 @Override
315 public void onTouchCanceled(TouchCancelEvent event) {
316 dropZIndeces();
317 Widget next = getNextWidget();
318 Widget previous = getPreviousWidget();
319 if (next != null) {
320 next.getElement().getStyle().setVisibility(Visibility.HIDDEN);
321 }
322
323 if (previous != null) {
324 previous.getElement().getStyle().setVisibility(Visibility.HIDDEN);
325 }
326
327 getVisibleChild().getElement().getStyle().clearLeft();
328 }
329 });
330 }
331
332 private void switchToApp(final SwipeEvent.DIRECTION direction) {
333
334 final Widget newVisibleWidget = direction == DIRECTION.LEFT_TO_RIGHT ? getPreviousWidget() : getNextWidget();
335 SlideAnimation slideAnimation = new SlideAnimation(false, true);
336 slideAnimation.addCallback(new JQueryCallback() {
337 @Override
338 public void execute(JQueryWrapper query) {
339
340 showChild(newVisibleWidget);
341 dropZIndeces();
342
343 MagnoliaTabSheetConnector appConnector = (MagnoliaTabSheetConnector) Util.findConnectorFor(newVisibleWidget);
344 listener.setCurrentApp(appConnector.getState().name);
345 }
346 });
347
348 Element targetElement = getVisibleChild().getElement();
349 slideAnimation.setTargetValue(getOffsetWidth() * (direction == DIRECTION.LEFT_TO_RIGHT ? 1 : -1));
350 slideAnimation.run(450, targetElement);
351
352 if (direction == DIRECTION.RIGHT_TO_LEFT && getWidgetCount() > 2) {
353 final SlideAnimation slideNewVisibleToEdgeAnimation = new SlideAnimation(false, true);
354 slideNewVisibleToEdgeAnimation.setTargetValue(0);
355 slideNewVisibleToEdgeAnimation.run(500, newVisibleWidget.getElement());
356 }
357
358 }
359
360 private void processSwipe(int translationValue) {
361 if (getWidgetCount() > 1) {
362 JQueryWrapper.select(getVisibleChild()).setCss("-webkit-transform", "translate3d(" + translationValue + "px,0,0)");
363 showCandidateApp(translationValue);
364 }
365 }
366
367 private void showCandidateApp(int translationValue) {
368 final Widget nextWidget = getNextWidget();
369 final Widget previousWidget = getPreviousWidget();
370 boolean isNext = translationValue < 0;
371 if (isNext) {
372 nextWidget.getElement().getStyle().setZIndex(250);
373 getVisibleChild().getElement().getStyle().setZIndex(251);
374 } else {
375 previousWidget.getElement().getStyle().setZIndex(250);
376 getVisibleChild().getElement().getStyle().setZIndex(251);
377 }
378
379 if (isNext && getWidgetCount() > 2) {
380 JQueryWrapper.select(nextWidget).setCss("-webkit-transform", "translate3d(" + (translationValue + getVisibleChild().getOffsetWidth()) + "px,0,0)");
381 }
382
383 nextWidget.getElement().getStyle().setVisibility(isNext || nextWidget == previousWidget ? Visibility.VISIBLE : Visibility.HIDDEN);
384 previousWidget.getElement().getStyle().setVisibility(!isNext || nextWidget == previousWidget ? Visibility.VISIBLE : Visibility.HIDDEN);
385 }
386
387 private Widget getNextWidget() {
388 int index = getWidgetIndex(getVisibleChild());
389 return getWidget((index + 1) % getWidgetCount());
390 }
391
392 private Widget getPreviousWidget() {
393 int index = getWidgetIndex(getVisibleChild());
394 int count = getWidgetCount();
395 return getWidget((index + (count - 1)) % count);
396 }
397
398 private void dropZIndeces() {
399 final Iterator<Widget> it = iterator();
400 while (it.hasNext()) {
401 it.next().getElement().getStyle().clearZIndex();
402 }
403 }
404
405 @Override
406 public HandlerRegistration addSwipeStartHandler(SwipeStartHandler handler) {
407 return addHandler(handler, SwipeStartEvent.getType());
408 }
409
410 @Override
411 public HandlerRegistration addSwipeMoveHandler(SwipeMoveHandler handler) {
412 return addHandler(handler, SwipeMoveEvent.getType());
413 }
414
415 @Override
416 public HandlerRegistration addSwipeEndHandler(SwipeEndHandler handler) {
417 return addHandler(handler, SwipeEndEvent.getType());
418 }
419
420 }