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