From: Johannes Dahlström Date: Mon, 11 Jun 2012 10:57:44 +0000 (+0300) Subject: Merge commit '16c91' X-Git-Tag: 7.0.0.alpha3~164 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=004d1182b58de32acafc6d23521dc5e6eba83b64;p=vaadin-framework.git Merge commit '16c91' --- 004d1182b58de32acafc6d23521dc5e6eba83b64 diff --cc src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java index c15a45a8d6,0000000000..8e78696f1b mode 100644,000000..100644 --- a/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java +++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java @@@ -1,518 -1,0 +1,517 @@@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui.accordion; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ConnectorMap; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate; +import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate.TouchScrollHandler; +import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector; +import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheetBase; + +public class VAccordion extends VTabsheetBase { + + public static final String CLASSNAME = "v-accordion"; + + private Set widgets = new HashSet(); + + HashMap lazyUpdateMap = new HashMap(); + + StackItem openTab = null; + + int selectedUIDLItemIndex = -1; + + private final TouchScrollHandler touchScrollHandler; + + public VAccordion() { + super(CLASSNAME); + touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this); + } + + @Override + protected void renderTab(UIDL tabUidl, int index, boolean selected, + boolean hidden) { + StackItem item; + int itemIndex; + if (getWidgetCount() <= index) { + // Create stackItem and render caption + item = new StackItem(tabUidl); + if (getWidgetCount() == 0) { + item.addStyleDependentName("first"); + } + itemIndex = getWidgetCount(); + add(item, getElement()); + } else { + item = getStackItem(index); + item = moveStackItemIfNeeded(item, index, tabUidl); + itemIndex = index; + } + item.updateCaption(tabUidl); + + item.setVisible(!hidden); + + if (selected) { + selectedUIDLItemIndex = itemIndex; + } + + if (tabUidl.getChildCount() > 0) { + lazyUpdateMap.put(item, tabUidl.getChildUIDL(0)); + } + } + + /** + * This method tries to find out if a tab has been rendered with a different + * index previously. If this is the case it re-orders the children so the + * same StackItem is used for rendering this time. E.g. if the first tab has + * been removed all tabs which contain cached content must be moved 1 step + * up to preserve the cached content. + * + * @param item + * @param newIndex + * @param tabUidl + * @return + */ + private StackItem moveStackItemIfNeeded(StackItem item, int newIndex, + UIDL tabUidl) { + UIDL tabContentUIDL = null; + ComponentConnector tabContent = null; + if (tabUidl.getChildCount() > 0) { + tabContentUIDL = tabUidl.getChildUIDL(0); + tabContent = client.getPaintable(tabContentUIDL); + } + + Widget itemWidget = item.getComponent(); + if (tabContent != null) { + if (tabContent != itemWidget) { + /* + * This is not the same widget as before, find out if it has + * been moved + */ + int oldIndex = -1; + StackItem oldItem = null; + for (int i = 0; i < getWidgetCount(); i++) { + Widget w = getWidget(i); + oldItem = (StackItem) w; + if (tabContent == oldItem.getComponent()) { + oldIndex = i; + break; + } + } + + if (oldIndex != -1 && oldIndex > newIndex) { + /* + * The tab has previously been rendered in another position + * so we must move the cached content to correct position. + * We move only items with oldIndex > newIndex to prevent + * moving items already rendered in this update. If for + * instance tabs 1,2,3 are removed and added as 3,2,1 we + * cannot re-use "1" when we get to the third tab. + */ + insert(oldItem, getElement(), newIndex, true); + return oldItem; + } + } + } else { + // Tab which has never been loaded. Must assure we use an empty + // StackItem + Widget oldWidget = item.getComponent(); + if (oldWidget != null) { + oldWidget.removeFromParent(); + } + } + return item; + } + + void open(int itemIndex) { + StackItem item = (StackItem) getWidget(itemIndex); + boolean alreadyOpen = false; + if (openTab != null) { + if (openTab.isOpen()) { + if (openTab == item) { + alreadyOpen = true; + } else { + openTab.close(); + } + } + } + if (!alreadyOpen) { + item.open(); + activeTabIndex = itemIndex; + openTab = item; + } + + // Update the size for the open tab + updateOpenTabSize(); + } + + void close(StackItem item) { + if (!item.isOpen()) { + return; + } + + item.close(); + activeTabIndex = -1; + openTab = null; + + } + + @Override + protected void selectTab(final int index, final UIDL contentUidl) { + StackItem item = getStackItem(index); + if (index != activeTabIndex) { + open(index); + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + } + item.setContent(contentUidl); + } + + public void onSelectTab(StackItem item) { + final int index = getWidgetIndex(item); + if (index != activeTabIndex && !disabled && !readonly + && !disabledTabKeys.contains(tabKeys.get(index))) { + addStyleDependentName("loading"); + client.updateVariable(id, "selected", "" + tabKeys.get(index), true); + } + } + + /** + * Sets the size of the open tab + */ + void updateOpenTabSize() { + if (openTab == null) { + return; + } + + // WIDTH + if (!isDynamicWidth()) { + openTab.setWidth("100%"); + } else { + openTab.setWidth(null); + } + + // HEIGHT + if (!isDynamicHeight()) { + int usedPixels = 0; + for (Widget w : getChildren()) { + StackItem item = (StackItem) w; + if (item == openTab) { + usedPixels += item.getCaptionHeight(); + } else { + // This includes the captionNode borders + usedPixels += item.getHeight(); + } + } + + int offsetHeight = getOffsetHeight(); + + int spaceForOpenItem = offsetHeight - usedPixels; + + if (spaceForOpenItem < 0) { + spaceForOpenItem = 0; + } + + openTab.setHeight(spaceForOpenItem); + } else { + openTab.setHeightFromWidget(); + + } + + } + + public void iLayout() { + if (openTab == null) { + return; + } + + if (isDynamicWidth()) { + int maxWidth = 40; + for (Widget w : getChildren()) { + StackItem si = (StackItem) w; + int captionWidth = si.getCaptionWidth(); + if (captionWidth > maxWidth) { + maxWidth = captionWidth; + } + } + int widgetWidth = openTab.getWidgetWidth(); + if (widgetWidth > maxWidth) { + maxWidth = widgetWidth; + } + super.setWidth(maxWidth + "px"); + openTab.setWidth(maxWidth); + } + } + + /** + * A StackItem has always two children, Child 0 is a VCaption, Child 1 is + * the actual child widget. + */ + protected class StackItem extends ComplexPanel implements ClickHandler { + + public void setHeight(int height) { + if (height == -1) { + super.setHeight(""); + DOM.setStyleAttribute(content, "height", "0px"); + } else { + super.setHeight((height + getCaptionHeight()) + "px"); + DOM.setStyleAttribute(content, "height", height + "px"); + DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px"); + + } + } + + public Widget getComponent() { + if (getWidgetCount() < 2) { + return null; + } + return getWidget(1); + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + } + + public void setHeightFromWidget() { + Widget widget = getChildWidget(); + if (widget == null) { + return; + } + + int paintableHeight = widget.getElement().getOffsetHeight(); + setHeight(paintableHeight); + + } + + /** + * Returns caption width including padding + * + * @return + */ + public int getCaptionWidth() { + if (caption == null) { + return 0; + } + + int captionWidth = caption.getRequiredWidth(); + int padding = Util.measureHorizontalPaddingAndBorder( + caption.getElement(), 18); + return captionWidth + padding; + } + + public void setWidth(int width) { + if (width == -1) { + super.setWidth(""); + } else { + super.setWidth(width + "px"); + } + } + + public int getHeight() { + return getOffsetHeight(); + } + + public int getCaptionHeight() { + return captionNode.getOffsetHeight(); + } + + private VCaption caption; + private boolean open = false; + private Element content = DOM.createDiv(); + private Element captionNode = DOM.createDiv(); + + public StackItem(UIDL tabUidl) { + setElement(DOM.createDiv()); + caption = new VCaption(client); + caption.addClickHandler(this); + super.add(caption, captionNode); + DOM.appendChild(captionNode, caption.getElement()); + DOM.appendChild(getElement(), captionNode); + DOM.appendChild(getElement(), content); + + getElement().addClassName(CLASSNAME + "-item"); + captionNode.addClassName(CLASSNAME + "-item-caption"); + content.addClassName(CLASSNAME + "-item-content"); + + touchScrollHandler.addElement(getContainerElement()); + + sinkEvents(Event.TOUCHEVENTS | Event.MOUSEEVENTS); + + close(); + } + + @Override + public void onBrowserEvent(Event event) { + onSelectTab(this); + } + + public Element getContainerElement() { + return content; + } + + public Widget getChildWidget() { + if (getWidgetCount() > 1) { + return getWidget(1); + } else { + return null; + } + } + + public void replaceWidget(Widget newWidget) { + if (getWidgetCount() > 1) { + Widget oldWidget = getWidget(1); + ComponentConnector oldPaintable = ConnectorMap.get(client) + .getConnector(oldWidget); + ConnectorMap.get(client).unregisterConnector(oldPaintable); + widgets.remove(oldWidget); + remove(1); + } + add(newWidget, content); + widgets.add(newWidget); + } + + public void open() { + open = true; + DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px"); + DOM.setStyleAttribute(content, "left", "0px"); + DOM.setStyleAttribute(content, "visibility", ""); + addStyleDependentName("open"); + } + + public void hide() { + DOM.setStyleAttribute(content, "visibility", "hidden"); + } + + public void close() { + DOM.setStyleAttribute(content, "visibility", "hidden"); + DOM.setStyleAttribute(content, "top", "-100000px"); + DOM.setStyleAttribute(content, "left", "-100000px"); + removeStyleDependentName("open"); + setHeight(-1); + setWidth(""); + open = false; + } + + public boolean isOpen() { + return open; + } + + public void setContent(UIDL contentUidl) { + final ComponentConnector newPntbl = client + .getPaintable(contentUidl); + Widget newWidget = newPntbl.getWidget(); + if (getChildWidget() == null) { + add(newWidget, content); + widgets.add(newWidget); + } else if (getChildWidget() != newWidget) { + replaceWidget(newWidget); + } + if (contentUidl.getBooleanAttribute("cached")) { + /* + * The size of a cached, relative sized component must be + * updated to report correct size. + */ + client.handleComponentRelativeSize(newPntbl.getWidget()); + } + if (isOpen() && isDynamicHeight()) { + setHeightFromWidget(); + } + } + + @Override + public void onClick(ClickEvent event) { + onSelectTab(this); + } + + public void updateCaption(UIDL uidl) { + // TODO need to call this because the caption does not have an owner + caption.updateCaptionWithoutOwner( + uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_CAPTION), + uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DISABLED), + uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION), + uidl.hasAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE), + uidl.getStringAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ICON)); + } + + public int getWidgetWidth() { + return DOM.getFirstChild(content).getOffsetWidth(); + } + + public boolean contains(ComponentConnector p) { + return (getChildWidget() == p.getWidget()); + } + + public boolean isCaptionVisible() { + return caption.isVisible(); + } + + } + + @Override + protected void clearPaintables() { + clear(); + } + + boolean isDynamicWidth() { + ComponentConnector paintable = ConnectorMap.get(client).getConnector( + this); + return paintable.isUndefinedWidth(); + } + + boolean isDynamicHeight() { + ComponentConnector paintable = ConnectorMap.get(client).getConnector( + this); + return paintable.isUndefinedHeight(); + } + + @Override + @SuppressWarnings("unchecked") + protected Iterator getWidgetIterator() { + return widgets.iterator(); + } + + @Override + protected int getTabCount() { + return getWidgetCount(); + } + + @Override + protected void removeTab(int index) { + StackItem item = getStackItem(index); + remove(item); - - touchScrollHandler.addElement(item.getContainerElement()); ++ touchScrollHandler.removeElement(item.getContainerElement()); + } + + @Override + protected ComponentConnector getTab(int index) { + if (index < getWidgetCount()) { + Widget w = getStackItem(index); + return ConnectorMap.get(client).getConnector(w); + } + + return null; + } + + StackItem getStackItem(int index) { + return (StackItem) getWidget(index); + } + +}