Java tutorial
/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.user.client.ui; import com.google.gwt.animation.client.Animation; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; /** * A panel that displays all of its child widgets in a 'deck', where only one * can be visible at a time. It is used by * {@link com.google.gwt.user.client.ui.TabPanel}. * * <p> * Once a widget has been added to a DeckPanel, its visibility, width, and * height attributes will be manipulated. When the widget is removed from the * DeckPanel, it will be visible, and its width and height attributes will be * cleared. * </p> */ public class DeckPanel extends ComplexPanel implements HasAnimation, InsertPanel.ForIsWidget { /** * An {@link Animation} used to slide in the new content. */ private static class SlideAnimation extends Animation { /** * The {@link Element} holding the {@link Widget} with a lower index. */ private Element container1 = null; /** * The {@link Element} holding the {@link Widget} with a higher index. */ private Element container2 = null; /** * A boolean indicating whether container1 is growing or shrinking. */ private boolean growing = false; /** * The fixed height of a {@link TabPanel} in pixels. If the {@link TabPanel} * does not have a fixed height, this will be set to -1. */ private int fixedHeight = -1; /** * The old {@link Widget} that is being hidden. */ private Widget oldWidget = null; /** * Switch to a new {@link Widget}. * * @param oldWidget the {@link Widget} to hide * @param newWidget the {@link Widget} to show * @param animate true to animate, false to switch instantly */ public void showWidget(Widget oldWidget, Widget newWidget, boolean animate) { // Immediately complete previous animation cancel(); // Get the container and index of the new widget Element newContainer = getContainer(newWidget); int newIndex = DOM.getChildIndex(DOM.getParent(newContainer), newContainer); // If we aren't showing anything, don't bother with the animation if (oldWidget == null) { UIObject.setVisible(newContainer, true); newWidget.setVisible(true); return; } this.oldWidget = oldWidget; // Get the container and index of the old widget Element oldContainer = getContainer(oldWidget); int oldIndex = DOM.getChildIndex(DOM.getParent(oldContainer), oldContainer); // Figure out whether to grow or shrink the container if (newIndex > oldIndex) { container1 = oldContainer; container2 = newContainer; growing = false; } else { container1 = newContainer; container2 = oldContainer; growing = true; } // Start the animation if (animate) { // Figure out if the deck panel has a fixed height com.google.gwt.dom.client.Element deckElem = container1.getParentElement(); int deckHeight = deckElem.getOffsetHeight(); if (growing) { fixedHeight = container2.getOffsetHeight(); container2.getStyle().setPropertyPx("height", Math.max(1, fixedHeight - 1)); } else { fixedHeight = container1.getOffsetHeight(); container1.getStyle().setPropertyPx("height", Math.max(1, fixedHeight - 1)); } if (deckElem.getOffsetHeight() != deckHeight) { fixedHeight = -1; } // Only scope to the deck if it's fixed height, otherwise it can affect // the rest of the page, even if it's not visible to the user. run(ANIMATION_DURATION, fixedHeight == -1 ? null : deckElem); } else { onInstantaneousRun(); } // We call newWidget.setVisible(true) immediately after showing the // widget's container so users can delay render their widget. Ultimately, // we should have a better way of handling this, but we need to call // setVisible for legacy support. newWidget.setVisible(true); } @Override protected void onComplete() { if (growing) { DOM.setStyleAttribute(container1, "height", "100%"); UIObject.setVisible(container1, true); UIObject.setVisible(container2, false); DOM.setStyleAttribute(container2, "height", "100%"); } else { UIObject.setVisible(container1, false); DOM.setStyleAttribute(container1, "height", "100%"); DOM.setStyleAttribute(container2, "height", "100%"); UIObject.setVisible(container2, true); } DOM.setStyleAttribute(container1, "overflow", "visible"); DOM.setStyleAttribute(container2, "overflow", "visible"); container1 = null; container2 = null; hideOldWidget(); } @Override protected void onStart() { // Start the animation DOM.setStyleAttribute(container1, "overflow", "hidden"); DOM.setStyleAttribute(container2, "overflow", "hidden"); onUpdate(0.0); UIObject.setVisible(container1, true); UIObject.setVisible(container2, true); } @Override protected void onUpdate(double progress) { if (!growing) { progress = 1.0 - progress; } // Container1 expands (shrinks) to its target height int height1; int height2; if (fixedHeight == -1) { height1 = (int) (progress * DOM.getElementPropertyInt(container1, "scrollHeight")); height2 = (int) ((1.0 - progress) * DOM.getElementPropertyInt(container2, "scrollHeight")); } else { height1 = (int) (progress * fixedHeight); height2 = fixedHeight - height1; } // Issue 2339: If the height is 0px, IE7 will display the entire content // widget instead of hiding it completely. if (height1 == 0) { height1 = 1; height2 = Math.max(1, height2 - 1); } else if (height2 == 0) { height2 = 1; height1 = Math.max(1, height1 - 1); } DOM.setStyleAttribute(container1, "height", height1 + "px"); DOM.setStyleAttribute(container2, "height", height2 + "px"); } /** * Hide the old widget when the animation completes. */ private void hideOldWidget() { // Issue 2510: Hiding the widget isn't necessary because we hide its // wrapper, but its in here for legacy support. oldWidget.setVisible(false); oldWidget = null; } private void onInstantaneousRun() { UIObject.setVisible(container1, growing); UIObject.setVisible(container2, !growing); container1 = null; container2 = null; hideOldWidget(); } } /** * The duration of the animation. */ private static final int ANIMATION_DURATION = 350; /** * The {@link Animation} used to slide in the new {@link Widget}. */ private static SlideAnimation slideAnimation; /** * The the container {@link Element} around a {@link Widget}. * * @param w the {@link Widget} * @return the container {@link Element} */ private static Element getContainer(Widget w) { return DOM.getParent(w.getElement()); } private boolean isAnimationEnabled = false; private Widget visibleWidget; /** * Creates an empty deck panel. */ public DeckPanel() { setElement(DOM.createDiv()); } @Override public void add(Widget w) { Element container = createWidgetContainer(); DOM.appendChild(getElement(), container); // The order of these methods is very important. In order to preserve // backward compatibility, the offsetWidth and offsetHeight of the child // widget should be defined (greater than zero) when w.onLoad() is called. // As a result, we first initialize the container with a height of 0px, then // we attach the child widget to the container. See Issue 2321 for more // details. super.add(w, container); // After w.onLoad is called, it is safe to make the container invisible and // set the height of the container and widget to 100%. finishWidgetInitialization(container, w); } /** * Gets the index of the currently-visible widget. * * @return the visible widget's index */ public int getVisibleWidget() { return getWidgetIndex(visibleWidget); } public void insert(IsWidget w, int beforeIndex) { insert(asWidgetOrNull(w), beforeIndex); } public void insert(Widget w, int beforeIndex) { Element container = createWidgetContainer(); DOM.insertChild(getElement(), container, beforeIndex); // See add(Widget) for important comments insert(w, container, beforeIndex, true); finishWidgetInitialization(container, w); } public boolean isAnimationEnabled() { return isAnimationEnabled; } @Override public boolean remove(Widget w) { Element container = getContainer(w); boolean removed = super.remove(w); if (removed) { resetChildWidget(w); DOM.removeChild(getElement(), container); if (visibleWidget == w) { visibleWidget = null; } } return removed; } public void setAnimationEnabled(boolean enable) { isAnimationEnabled = enable; } /** * Shows the widget at the specified index. This causes the currently- visible * widget to be hidden. * * @param index the index of the widget to be shown */ public void showWidget(int index) { checkIndexBoundsForAccess(index); Widget oldWidget = visibleWidget; visibleWidget = getWidget(index); if (visibleWidget != oldWidget) { if (slideAnimation == null) { slideAnimation = new SlideAnimation(); } slideAnimation.showWidget(oldWidget, visibleWidget, isAnimationEnabled && isAttached()); } } /** * Setup the container around the widget. */ private Element createWidgetContainer() { Element container = DOM.createDiv(); DOM.setStyleAttribute(container, "width", "100%"); DOM.setStyleAttribute(container, "height", "0px"); DOM.setStyleAttribute(container, "padding", "0px"); DOM.setStyleAttribute(container, "margin", "0px"); return container; } /** * Setup the container around the widget. */ private void finishWidgetInitialization(Element container, Widget w) { UIObject.setVisible(container, false); DOM.setStyleAttribute(container, "height", "100%"); // Set 100% by default. Element element = w.getElement(); if (DOM.getStyleAttribute(element, "width").equals("")) { w.setWidth("100%"); } if (DOM.getStyleAttribute(element, "height").equals("")) { w.setHeight("100%"); } // Issue 2510: Hiding the widget isn't necessary because we hide its // wrapper, but it's in here for legacy support. w.setVisible(false); } /** * Reset the dimensions of the widget when it is removed. */ private void resetChildWidget(Widget w) { w.setSize("", ""); w.setVisible(true); } }