diff options
author | Felype Santiago Ferreira <felype@vaadin.com> | 2014-02-26 16:05:37 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2014-04-08 06:34:55 +0000 |
commit | f32e3535c6ef73bca33f274e9906f57aa178d22a (patch) | |
tree | ba2453a82f97a2c68f35d363ed2c553d84d5bae9 | |
parent | ef8920661cc9d958c69cfedd0a74ce580a2a84ed (diff) | |
download | vaadin-framework-f32e3535c6ef73bca33f274e9906f57aa178d22a.tar.gz vaadin-framework-f32e3535c6ef73bca33f274e9906f57aa178d22a.zip |
Update Accordion and TabSheet to use Vaadin 7 style. (#13402).
This change also adds subpart support for TabSheet and
converts a test to TB3.
Change-Id: I23b6c81686ea6587470d8019e89a85149ec0b068
19 files changed, 953 insertions, 612 deletions
diff --git a/client/src/com/vaadin/client/ui/VAccordion.java b/client/src/com/vaadin/client/ui/VAccordion.java index eb8cb8b2ed..cba08d8e6b 100644 --- a/client/src/com/vaadin/client/ui/VAccordion.java +++ b/client/src/com/vaadin/client/ui/VAccordion.java @@ -15,7 +15,6 @@ */ package com.vaadin.client.ui; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -28,43 +27,41 @@ 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.client.ComponentConnector; -import com.vaadin.client.ConnectorMap; -import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.VCaption; import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler; -import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants; -import com.vaadin.shared.ui.tabsheet.TabsheetConstants; +import com.vaadin.shared.ComponentConstants; +import com.vaadin.shared.ui.accordion.AccordionState; +import com.vaadin.shared.ui.tabsheet.TabState; +import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc; public class VAccordion extends VTabsheetBase { - public static final String CLASSNAME = "v-accordion"; + public static final String CLASSNAME = AccordionState.PRIMARY_STYLE_NAME; private Set<Widget> widgets = new HashSet<Widget>(); - /** For internal use only. May be removed or replaced in the future. */ - public HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>(); - - private StackItem openTab = null; + private StackItem openTab; /** For internal use only. May be removed or replaced in the future. */ - public int selectedUIDLItemIndex = -1; + public int selectedItemIndex = -1; private final TouchScrollHandler touchScrollHandler; public VAccordion() { super(CLASSNAME); + touchScrollHandler = TouchScrollDelegate.enableTouchScrolling(this); } @Override - public void renderTab(UIDL tabUidl, int index, boolean selected, - boolean hidden) { + public void renderTab(TabState tabState, int index) { StackItem item; int itemIndex; + if (getWidgetCount() <= index) { // Create stackItem and render caption - item = new StackItem(tabUidl); + item = new StackItem(); if (getWidgetCount() == 0) { item.addStyleDependentName("first"); } @@ -72,23 +69,19 @@ public class VAccordion extends VTabsheetBase { add(item, getElement()); } else { item = getStackItem(index); - item = moveStackItemIfNeeded(item, index, tabUidl); + itemIndex = index; } - item.updateCaption(tabUidl); + item.updateCaption(tabState); - item.updateTabStyleName(tabUidl - .getStringAttribute(TabsheetConstants.TAB_STYLE_NAME)); + item.updateTabStyleName(tabState.styleName); - item.setVisible(!hidden); - - if (selected) { - selectedUIDLItemIndex = itemIndex; - } + item.setVisible(tabState.visible); + } - if (tabUidl.getChildCount() > 0) { - lazyUpdateMap.put(item, tabUidl.getChildUIDL(0)); - } + @Override + public void selectTab(int index) { + selectedItemIndex = index; } @Override @@ -112,69 +105,6 @@ public class VAccordion extends VTabsheetBase { } } - /** - * 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.getWidget() != 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; - } - /** For internal use only. May be removed or replaced in the future. */ public void open(int itemIndex) { StackItem item = (StackItem) getWidget(itemIndex); @@ -210,10 +140,14 @@ public class VAccordion extends VTabsheetBase { 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); + + connector.getRpcProxy(TabsheetServerRpc.class).setSelected( + tabKeys.get(index).toString()); } } @@ -296,7 +230,7 @@ public class VAccordion extends VTabsheetBase { private Element captionNode = DOM.createDiv(); private String styleName; - public StackItem(UIDL tabUidl) { + public StackItem() { setElement(DOM.createDiv()); caption = new VCaption(client); caption.addClickHandler(this); @@ -375,10 +309,17 @@ public class VAccordion extends VTabsheetBase { return open; } - public void setContent(UIDL contentUidl) { - final ComponentConnector newPntbl = client - .getPaintable(contentUidl); - Widget newWidget = newPntbl.getWidget(); + /** + * Updates the content of the open tab of the accordion. + * + * This method is mostly for internal use and may change in future + * versions. + * + * @since 7.2 + * @param newWidget + * new content + */ + public void setContent(Widget newWidget) { if (getChildWidget() == null) { add(newWidget, content); widgets.add(newWidget); @@ -395,14 +336,19 @@ public class VAccordion extends VTabsheetBase { onSelectTab(this); } - public void updateCaption(UIDL uidl) { + public void updateCaption(TabState tabState) { // TODO need to call this because the caption does not have an owner caption.updateCaptionWithoutOwner( - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE), - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON)); + tabState.caption, + !tabState.enabled, + hasAttribute(tabState.description), + hasAttribute(tabState.componentError), + connector.getResourceUrl(ComponentConstants.ICON_RESOURCE + + tabState.key)); + } + + private boolean hasAttribute(String string) { + return string != null && !string.trim().isEmpty(); } /** @@ -450,18 +396,6 @@ public class VAccordion extends VTabsheetBase { 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 public Iterator<Widget> getWidgetIterator() { return widgets.iterator(); @@ -488,7 +422,7 @@ public class VAccordion extends VTabsheetBase { } Widget w = stackItem.getChildWidget(); if (w != null) { - return ConnectorMap.get(client).getConnector(w); + return getConnectorForWidget(w); } } diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java index 4da5e4bfe0..f2e4003e7f 100644 --- a/client/src/com/vaadin/client/ui/VTabsheet.java +++ b/client/src/com/vaadin/client/ui/VTabsheet.java @@ -43,6 +43,8 @@ import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.regexp.shared.MatchResult; +import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; @@ -53,21 +55,23 @@ import com.google.gwt.user.client.ui.impl.FocusImpl; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.ComponentConnector; -import com.vaadin.client.ConnectorMap; import com.vaadin.client.Focusable; import com.vaadin.client.TooltipInfo; -import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.VCaption; +import com.vaadin.client.VTooltip; import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.EventId; +import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.ComponentStateUtil; -import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants; -import com.vaadin.shared.ui.tabsheet.TabsheetConstants; +import com.vaadin.shared.ui.tabsheet.TabState; +import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc; +import com.vaadin.shared.ui.tabsheet.TabsheetState; public class VTabsheet extends VTabsheetBase implements Focusable, - FocusHandler, BlurHandler, KeyDownHandler { + FocusHandler, BlurHandler, KeyDownHandler, SubPartAware { private static class VCloseEvent { private Tab tab; @@ -140,8 +144,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, DOM.appendChild(td, div); - tabCaption = new TabCaption(this, getTabsheet() - .getApplicationConnection()); + tabCaption = new TabCaption(this); add(tabCaption); Roles.getTabRole().setAriaLabelledbyProperty(getElement(), @@ -228,12 +231,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable, return tabBar.getTabsheet(); } - public void updateFromUIDL(UIDL tabUidl) { - tabCaption.updateCaption(tabUidl); - + private void updateFromState(TabState tabState) { + tabCaption.update(tabState); // Apply the styleName set for the tab - String newStyleName = tabUidl - .getStringAttribute(TabsheetConstants.TAB_STYLE_NAME); + String newStyleName = tabState.styleName; // Find the nth td element if (newStyleName != null && !newStyleName.isEmpty()) { if (!newStyleName.equals(styleName)) { @@ -253,7 +254,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, styleName = null; } - String newId = tabUidl.getStringAttribute("id"); + String newId = tabState.id; if (newId != null && !newId.isEmpty()) { td.setId(newId); id = newId; @@ -315,38 +316,45 @@ public class VTabsheet extends VTabsheetBase implements Focusable, private Element closeButton; private Tab tab; - TabCaption(Tab tab, ApplicationConnection client) { - super(client); + TabCaption(Tab tab) { + super(tab.getTabsheet().connector.getConnection()); this.tab = tab; AriaHelper.ensureHasId(getElement()); } - public boolean updateCaption(UIDL uidl) { - if (uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION) - || uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE)) { - setTooltipInfo(new TooltipInfo( - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION), - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE))); + private boolean update(TabState tabState) { + if (tabState.description != null + || tabState.componentError != null) { + setTooltipInfo(new TooltipInfo(tabState.description, + tabState.componentError)); } else { setTooltipInfo(null); } // TODO need to call this instead of super because the caption does // not have an owner - boolean ret = updateCaptionWithoutOwner( - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION), - uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE), - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON), - uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON_ALT)); - - setClosable(uidl.hasAttribute("closable")); + String captionString = tabState.caption.isEmpty() ? null + : tabState.caption; + boolean ret = updateCaptionWithoutOwner(captionString, + !tabState.enabled, + hasAttribute(tabState.description), + hasAttribute(tabState.componentError), + tab.getTabsheet().connector + .getResourceUrl(ComponentConstants.ICON_RESOURCE + + tabState.key), + tabState.iconAltText + ); + + setClosable(tabState.closable); return ret; } + private boolean hasAttribute(String string) { + return string != null && !string.trim().isEmpty(); + } + private VTabsheet getTabsheet() { return tab.getTabsheet(); } @@ -637,12 +645,21 @@ public class VTabsheet extends VTabsheetBase implements Focusable, currentFirst.recalculateCaptionWidth(); return nextVisible; } + + private void recalculateCaptionWidths() { + for (int i = 0; i < getTabCount(); ++i) { + getTab(i).recalculateCaptionWidth(); + } + } } - public static final String CLASSNAME = "v-tabsheet"; + // TODO using the CLASSNAME directly makes primaryStyleName for TabSheet of + // very limited use - all use of style names should be refactored in the + // future + public static final String CLASSNAME = TabsheetState.PRIMARY_STYLE_NAME; - public static final String TABS_CLASSNAME = "v-tabsheet-tabcontainer"; - public static final String SCROLLER_CLASSNAME = "v-tabsheet-scroller"; + public static final String TABS_CLASSNAME = CLASSNAME + "-tabcontainer"; + public static final String SCROLLER_CLASSNAME = CLASSNAME + "-scroller"; /** For internal use only. May be removed or replaced in the future. */ // tabbar and 'scroller' container @@ -670,7 +687,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, final TabBar tb = new TabBar(this); /** For internal use only. May be removed or replaced in the future. */ - public final VTabsheetPanel tp = new VTabsheetPanel(); + protected final VTabsheetPanel tabPanel = new VTabsheetPanel(); /** For internal use only. May be removed or replaced in the future. */ public final Element contentNode; @@ -681,12 +698,16 @@ public class VTabsheet extends VTabsheetBase implements Focusable, private String currentStyle; + /** For internal use only. May be removed or replaced in the future. */ + private int focusedTabIndex = 0; + /** * @return Whether the tab could be selected or not. */ private boolean canSelectTab(final int tabIndex) { Tab tab = tb.getTab(tabIndex); - if (client == null || disabled || waitingForResponse) { + if (getApplicationConnection() == null || disabled + || waitingForResponse) { return false; } if (!tab.isEnabledOnServer() || tab.isHiddenOnServer()) { @@ -713,22 +734,56 @@ public class VTabsheet extends VTabsheetBase implements Focusable, addStyleDependentName("loading"); // Hide the current contents so a loading indicator can be shown // instead - Widget currentlyDisplayedWidget = tp.getWidget(tp - .getVisibleWidget()); - currentlyDisplayedWidget.getElement().getParentElement().getStyle() - .setVisibility(Visibility.HIDDEN); - client.updateVariable(id, "selected", tabKeys.get(tabIndex) - .toString(), true); + getCurrentlyDisplayedWidget().getElement().getParentElement() + .getStyle().setVisibility(Visibility.HIDDEN); + + getRpcProxy().setSelected(tabKeys.get(tabIndex).toString()); + waitingForResponse = true; tb.getTab(tabIndex).focus(); // move keyboard focus to active tab } } + /** + * Returns the currently displayed widget in the tab panel. + * + * @since 7.2 + * @return currently displayed content widget + */ + public Widget getCurrentlyDisplayedWidget() { + return tabPanel.getWidget(tabPanel.getVisibleWidget()); + } + + /** + * Returns the client to server RPC proxy for the tabsheet. + * + * @since 7.2 + * @return RPC proxy + */ + protected TabsheetServerRpc getRpcProxy() { + return connector.getRpcProxy(TabsheetServerRpc.class); + } + + /** + * For internal use only. + * + * Avoid using this method directly and use appropriate superclass methods + * where applicable. + * + * @deprecated since 7.2 - use more specific methods instead (getRpcProxy(), + * getConnectorForWidget(Widget) etc.) + * @return ApplicationConnection + */ + @Deprecated public ApplicationConnection getApplicationConnection() { return client; } + private VTooltip getVTooltip() { + return getApplicationConnection().getVTooltip(); + } + public void tabSizeMightHaveChanged(Tab tab) { // icon onloads may change total width of tabsheet if (isDynamicWidth()) { @@ -739,19 +794,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } void sendTabClosedEvent(int tabIndex) { - client.updateVariable(id, "close", tabKeys.get(tabIndex), true); - } - - boolean isDynamicWidth() { - ComponentConnector paintable = ConnectorMap.get(client).getConnector( - this); - return paintable.isUndefinedWidth(); - } - - boolean isDynamicHeight() { - ComponentConnector paintable = ConnectorMap.get(client).getConnector( - this); - return paintable.isUndefinedHeight(); + getRpcProxy().closeTab(tabKeys.get(tabIndex)); } public VTabsheet() { @@ -785,7 +828,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, DOM.appendChild(getElement(), tabs); // Tabs - tp.setStyleName(CLASSNAME + "-tabsheetpanel"); + tabPanel.setStyleName(CLASSNAME + "-tabsheetpanel"); contentNode = DOM.createDiv(); Roles.getTabpanelRole().set(contentNode); @@ -801,7 +844,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, DOM.appendChild(scroller, scrollerNext); DOM.appendChild(getElement(), contentNode); - add(tp, contentNode); + add(tabPanel, contentNode); DOM.appendChild(getElement(), deco); DOM.appendChild(tabs, scroller); @@ -851,7 +894,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } /** For internal use only. May be removed or replaced in the future. */ - public void handleStyleNames(UIDL uidl, AbstractComponentState state) { + public void handleStyleNames(AbstractComponentState state) { // Add proper stylenames for all elements (easier to prevent unwanted // style inheritance) if (ComponentStateUtil.hasStyles(state)) { @@ -882,14 +925,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable, + "-content"); DOM.setElementProperty(deco, "className", CLASSNAME + "-deco"); } - - if (uidl.hasAttribute("hidetabs")) { - tb.setVisible(false); - addStyleName(CLASSNAME + "-hidetabs"); - } else { - tb.setVisible(true); - removeStyleName(CLASSNAME + "-hidetabs"); - } } /** For internal use only. May be removed or replaced in the future. */ @@ -906,16 +941,16 @@ public class VTabsheet extends VTabsheetBase implements Focusable, int tabsWidth = tb.getOffsetWidth() - spacerWidth + spacerMinWidth; // Find content width - Style style = tp.getElement().getStyle(); + Style style = tabPanel.getElement().getStyle(); String overflow = style.getProperty("overflow"); style.setProperty("overflow", "hidden"); style.setPropertyPx("width", tabsWidth); - boolean hasTabs = tp.getWidgetCount() > 0; + boolean hasTabs = tabPanel.getWidgetCount() > 0; Style wrapperstyle = null; if (hasTabs) { - wrapperstyle = tp.getWidget(tp.getVisibleWidget()).getElement() + wrapperstyle = getCurrentlyDisplayedWidget().getElement() .getParentElement().getStyle(); wrapperstyle.setPropertyPx("width", tabsWidth); } @@ -923,7 +958,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, int contentWidth = 0; if (hasTabs) { - contentWidth = tp.getWidget(tp.getVisibleWidget()).getOffsetWidth(); + contentWidth = getCurrentlyDisplayedWidget().getOffsetWidth(); } style.setProperty("overflow", overflow); @@ -946,26 +981,22 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } @Override - public void renderTab(final UIDL tabUidl, int index, boolean selected, - boolean hidden) { + public void renderTab(final TabState tabState, int index) { Tab tab = tb.getTab(index); if (tab == null) { tab = tb.addTab(); } - if (selected) { - tb.selectTab(index); - renderContent(tabUidl.getChildUIDL(0)); - } - tab.updateFromUIDL(tabUidl); + + tab.updateFromState(tabState); tab.setEnabledOnServer((!disabledTabKeys.contains(tabKeys.get(index)))); - tab.setHiddenOnServer(hidden); + tab.setHiddenOnServer(!tabState.visible); if (scrolledOutOfView(index)) { // Should not set tabs visible if they are scrolled out of view - hidden = true; + tabState.visible = false; } // Set the current visibility of the tab (in the browser) - tab.setVisible(!hidden); + tab.setVisible(tabState.visible); /* * Force the width of the caption container so the content will not wrap @@ -985,23 +1016,29 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } } - private void renderContent(final UIDL contentUIDL) { - final ComponentConnector content = client.getPaintable(contentUIDL); - Widget newWidget = content.getWidget(); + /** + * Renders the widget content for a tab sheet. + * + * @param newWidget + */ + public void renderContent(Widget newWidget) { + assert tabPanel.getWidgetCount() <= 1; - assert tp.getWidgetCount() <= 1; + if (null == newWidget) { + newWidget = new SimplePanel(); + } - if (tp.getWidgetCount() == 0) { - tp.add(newWidget); - } else if (tp.getWidget(0) != newWidget) { - tp.remove(0); - tp.add(newWidget); + if (tabPanel.getWidgetCount() == 0) { + tabPanel.add(newWidget); + } else if (tabPanel.getWidget(0) != newWidget) { + tabPanel.remove(0); + tabPanel.add(newWidget); } - assert tp.getWidgetCount() <= 1; + assert tabPanel.getWidgetCount() <= 1; // There's never any other index than 0, but maintaining API for now - tp.showWidget(0); + tabPanel.showWidget(0); VTabsheet.this.iLayout(); updateOpenTabSize(); @@ -1058,7 +1095,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, */ minWidth = tb.getOffsetWidth() - getContentAreaBorderWidth(); } - tp.fixVisibleTabSize(width, height, minWidth); + tabPanel.fixVisibleTabSize(width, height, minWidth); } @@ -1149,13 +1186,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable, while (i > 0) { tb.removeTab(--i); } - tp.clear(); + tabPanel.clear(); } @Override public Iterator<Widget> getWidgetIterator() { - return tp.iterator(); + return tabPanel.iterator(); } private int borderW = -1; @@ -1175,9 +1212,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable, @Override public ComponentConnector getTab(int index) { - if (tp.getWidgetCount() > index) { - Widget widget = tp.getWidget(index); - return ConnectorMap.get(client).getConnector(widget); + if (tabPanel.getWidgetCount() > index) { + Widget widget = tabPanel.getWidget(index); + return getConnectorForWidget(widget); } return null; } @@ -1190,14 +1227,19 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } @Override + public void selectTab(int index) { + tb.selectTab(index); + } + + @Override public void onBlur(BlurEvent event) { - getApplicationConnection().getVTooltip().hideTooltip(); + getVTooltip().hideTooltip(); if (focusedTab != null && focusedTab == event.getSource()) { focusedTab.removeAssistiveDescription(); focusedTab = null; - if (client.hasEventListeners(this, EventId.BLUR)) { - client.updateVariable(id, EventId.BLUR, "", true); + if (connector.hasEventListener(EventId.BLUR)) { + connector.getRpcProxy(FocusAndBlurServerRpc.class).blur(); } } } @@ -1206,15 +1248,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable, public void onFocus(FocusEvent event) { if (focusedTab == null && event.getSource() instanceof Tab) { focusedTab = (Tab) event.getSource(); - if (client.hasEventListeners(this, EventId.FOCUS)) { - client.updateVariable(id, EventId.FOCUS, "", true); + if (connector.hasEventListener(EventId.FOCUS)) { + connector.getRpcProxy(FocusAndBlurServerRpc.class).focus(); } if (focusedTab.hasTooltip()) { - focusedTab.setAssistiveDescription(getApplicationConnection() - .getVTooltip().getUniqueId()); - getApplicationConnection().getVTooltip().showAssistive( - focusedTab.getTooltipInfo()); + focusedTab.setAssistiveDescription(getVTooltip().getUniqueId()); + getVTooltip().showAssistive(focusedTab.getTooltipInfo()); } } } @@ -1336,4 +1376,70 @@ public class VTabsheet extends VTabsheetBase implements Focusable, } } } + + /** + * Makes tab bar visible. + * + * @since 7.2 + */ + public void showTabs() { + tb.setVisible(true); + removeStyleName(CLASSNAME + "-hidetabs"); + tb.recalculateCaptionWidths(); + } + + /** + * Makes tab bar invisible. + * + * @since 7.2 + */ + public void hideTabs() { + tb.setVisible(false); + addStyleName(CLASSNAME + "-hidetabs"); + } + + /** Matches tab[ix] - used for extracting the index of the targeted tab */ + private static final RegExp SUBPART_TAB_REGEXP = RegExp + .compile("tab\\[(\\d+)](.*)"); + + @Override + public com.google.gwt.user.client.Element getSubPartElement(String subPart) { + if ("tabpanel".equals(subPart)) { + return DOM.asOld(tabPanel.getElement().getFirstChildElement()); + } else if (SUBPART_TAB_REGEXP.test(subPart)) { + MatchResult result = SUBPART_TAB_REGEXP.exec(subPart); + int tabIx = Integer.valueOf(result.getGroup(1)); + Tab tab = tb.getTab(tabIx); + if (tab != null) { + if ("/close".equals(result.getGroup(2))) { + if (tab.isClosable()) { + return tab.tabCaption.getCloseButton(); + } + } else { + return tab.tabCaption.getElement(); + } + } + } + return null; + } + + @Override + public String getSubPartName(com.google.gwt.user.client.Element subElement) { + if (tabPanel.getElement().equals(subElement.getParentElement()) + || tabPanel.getElement().equals(subElement)) { + return "tabpanel"; + } else { + for (int i = 0; i < tb.getTabCount(); ++i) { + Tab tab = tb.getTab(i); + if (tab.isClosable() + && tab.tabCaption.getCloseButton().isOrHasChild( + subElement)) { + return "tab[" + i + "]/close"; + } else if (tab.getElement().isOrHasChild(subElement)) { + return "tab[" + i + "]"; + } + } + } + return null; + } } diff --git a/client/src/com/vaadin/client/ui/VTabsheetBase.java b/client/src/com/vaadin/client/ui/VTabsheetBase.java index bcd8811c7d..6d9f78e87f 100644 --- a/client/src/com/vaadin/client/ui/VTabsheetBase.java +++ b/client/src/com/vaadin/client/ui/VTabsheetBase.java @@ -25,28 +25,28 @@ import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; -import com.vaadin.client.UIDL; +import com.vaadin.client.ConnectorMap; +import com.vaadin.shared.ui.tabsheet.TabState; public abstract class VTabsheetBase extends ComplexPanel { /** For internal use only. May be removed or replaced in the future. */ - public String id; - /** For internal use only. May be removed or replaced in the future. */ - public ApplicationConnection client; + protected ApplicationConnection client; /** For internal use only. May be removed or replaced in the future. */ - public final ArrayList<String> tabKeys = new ArrayList<String>(); + protected final ArrayList<String> tabKeys = new ArrayList<String>(); /** For internal use only. May be removed or replaced in the future. */ - public Set<String> disabledTabKeys = new HashSet<String>(); + protected Set<String> disabledTabKeys = new HashSet<String>(); /** For internal use only. May be removed or replaced in the future. */ - public int activeTabIndex = 0; + protected int activeTabIndex = 0; /** For internal use only. May be removed or replaced in the future. */ - public int focusedTabIndex = 0; + protected boolean disabled; /** For internal use only. May be removed or replaced in the future. */ - public boolean disabled; + protected boolean readonly; + /** For internal use only. May be removed or replaced in the future. */ - public boolean readonly; + protected AbstractComponentConnector connector; public VTabsheetBase(String classname) { setElement(DOM.createDiv()); @@ -61,14 +61,13 @@ public abstract class VTabsheetBase extends ComplexPanel { /** * Clears current tabs and contents */ - abstract protected void clearPaintables(); + protected abstract void clearPaintables(); /** * Implement in extending classes. This method should render needed elements * and set the visibility of the tab according to the 'selected' parameter. */ - public abstract void renderTab(final UIDL tabUidl, int index, - boolean selected, boolean hidden); + public abstract void renderTab(TabState tabState, int index); /** * Implement in extending classes. This method should return the number of @@ -87,4 +86,79 @@ public abstract class VTabsheetBase extends ComplexPanel { * tab with the specified index. */ public abstract void removeTab(int index); + + /** + * Returns true if the width of the widget is undefined, false otherwise. + * + * @since 7.2 + * @return true if width of the widget is determined by its content + */ + protected boolean isDynamicWidth() { + return getConnectorForWidget(this).isUndefinedWidth(); + } + + /** + * Returns true if the height of the widget is undefined, false otherwise. + * + * @since 7.2 + * @return true if width of the height is determined by its content + */ + protected boolean isDynamicHeight() { + return getConnectorForWidget(this).isUndefinedHeight(); + } + + /** + * Sets the connector that should be notified of events etc. + * + * For internal use only. This method may be removed or replaced in the + * future. + * + * @since 7.2 + * @param connector + */ + public void setConnector(AbstractComponentConnector connector) { + this.connector = connector; + } + + /** For internal use only. May be removed or replaced in the future. */ + public void clearTabKeys() { + tabKeys.clear(); + disabledTabKeys.clear(); + } + + /** For internal use only. May be removed or replaced in the future. */ + public void addTabKey(String key, boolean disabled) { + tabKeys.add(key); + if (disabled) { + disabledTabKeys.add(key); + } + } + + /** For internal use only. May be removed or replaced in the future. */ + public void setClient(ApplicationConnection client) { + this.client = client; + } + + /** For internal use only. May be removed or replaced in the future. */ + public void setActiveTabIndex(int activeTabIndex) { + this.activeTabIndex = activeTabIndex; + } + + /** For internal use only. May be removed or replaced in the future. */ + public void setEnabled(boolean enabled) { + disabled = !enabled; + } + + /** For internal use only. May be removed or replaced in the future. */ + public void setReadonly(boolean readonly) { + this.readonly = readonly; + } + + /** For internal use only. May be removed or replaced in the future. */ + protected ComponentConnector getConnectorForWidget(Widget widget) { + return ConnectorMap.get(client).getConnector(widget); + } + + /** For internal use only. May be removed or replaced in the future. */ + public abstract void selectTab(int index); } diff --git a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java index 99fbd07f9b..ce843dc22f 100644 --- a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java +++ b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java @@ -15,19 +15,17 @@ */ package com.vaadin.client.ui.accordion; -import java.util.Iterator; - -import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; -import com.vaadin.client.UIDL; import com.vaadin.client.Util; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VAccordion; import com.vaadin.client.ui.VAccordion.StackItem; import com.vaadin.client.ui.layout.MayScrollChildren; import com.vaadin.client.ui.tabsheet.TabsheetBaseConnector; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.accordion.AccordionState; import com.vaadin.ui.Accordion; @Connect(Accordion.class) @@ -35,35 +33,32 @@ public class AccordionConnector extends TabsheetBaseConnector implements SimpleManagedLayout, MayScrollChildren { @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - getWidget().selectedUIDLItemIndex = -1; - super.updateFromUIDL(uidl, client); + protected void init() { + super.init(); + getWidget().setConnector(this); + } + + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + /* * Render content after all tabs have been created and we know how large * the content area is */ - if (getWidget().selectedUIDLItemIndex >= 0) { + if (getWidget().selectedItemIndex >= 0) { StackItem selectedItem = getWidget().getStackItem( - getWidget().selectedUIDLItemIndex); - UIDL selectedTabUIDL = getWidget().lazyUpdateMap - .remove(selectedItem); - getWidget().open(getWidget().selectedUIDLItemIndex); + getWidget().selectedItemIndex); - selectedItem.setContent(selectedTabUIDL); - } else if (isRealUpdate(uidl) && getWidget().getOpenStackItem() != null) { - getWidget().close(getWidget().getOpenStackItem()); - } + getWidget().open(getWidget().selectedItemIndex); - // finally render possible hidden tabs - if (getWidget().lazyUpdateMap.size() > 0) { - for (Iterator iterator = getWidget().lazyUpdateMap.keySet() - .iterator(); iterator.hasNext();) { - StackItem item = (StackItem) iterator.next(); - item.setContent(getWidget().lazyUpdateMap.get(item)); + ComponentConnector contentConnector = getChildComponents().get(0); + if (contentConnector != null) { + selectedItem.setContent(contentConnector.getWidget()); } - getWidget().lazyUpdateMap.clear(); + } else if (getWidget().getOpenStackItem() != null) { + getWidget().close(getWidget().getOpenStackItem()); } - } @Override @@ -123,14 +118,24 @@ public class AccordionConnector extends TabsheetBaseConnector implements openTab.setHeight(spaceForOpenItem); } else { openTab.setHeightFromWidget(); - } - } + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ConnectorHierarchyChangeEvent. + * ConnectorHierarchyChangeHandler + * #onConnectorHierarchyChange(com.vaadin.client + * .ConnectorHierarchyChangeEvent) + */ @Override public void onConnectorHierarchyChange( ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { - // TODO Move code from updateFromUIDL to this method + } + + @Override + public AccordionState getState() { + return (AccordionState) super.getState(); } } diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java index 283bc1b63b..30c9e47c6e 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetBaseConnector.java @@ -19,31 +19,41 @@ import java.util.ArrayList; import java.util.Iterator; import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; -import com.vaadin.client.Paintable; -import com.vaadin.client.UIDL; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentContainerConnector; import com.vaadin.client.ui.VTabsheetBase; -import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants; +import com.vaadin.shared.ui.tabsheet.TabState; +import com.vaadin.shared.ui.tabsheet.TabsheetState; public abstract class TabsheetBaseConnector extends - AbstractComponentContainerConnector implements Paintable { + AbstractComponentContainerConnector { + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractConnector#init() + */ @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - getWidget().client = client; + protected void init() { + super.init(); - if (!isRealUpdate(uidl)) { - return; - } + getWidget().setClient(getConnection()); + } - // Update member references - getWidget().id = uidl.getId(); - getWidget().disabled = !isEnabled(); + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin + * .client.communication.StateChangeEvent) + */ + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); - // Render content - final UIDL tabs = uidl.getChildUIDL(0); + // Update member references + getWidget().setEnabled(isEnabled()); // Widgets in the TabSheet before update ArrayList<Widget> oldWidgets = new ArrayList<Widget>(); @@ -53,26 +63,22 @@ public abstract class TabsheetBaseConnector extends } // Clear previous values - getWidget().tabKeys.clear(); - getWidget().disabledTabKeys.clear(); + getWidget().clearTabKeys(); int index = 0; - for (final Iterator<Object> it = tabs.getChildIterator(); it.hasNext();) { - final UIDL tab = (UIDL) it.next(); - final String key = tab.getStringAttribute("key"); - final boolean selected = tab.getBooleanAttribute("selected"); - final boolean hidden = tab.getBooleanAttribute("hidden"); - - if (tab.getBooleanAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED)) { - getWidget().disabledTabKeys.add(key); - } + for (TabState tab : getState().tabs) { + final String key = tab.key; + final boolean selected = key.equals(getState().selected); - getWidget().tabKeys.add(key); + getWidget().addTabKey(key, !tab.enabled && tab.visible); if (selected) { - getWidget().activeTabIndex = index; + getWidget().setActiveTabIndex(index); + } + getWidget().renderTab(tab, index); + if (selected) { + getWidget().selectTab(index); } - getWidget().renderTab(tab, index, selected, hidden); index++; } @@ -104,4 +110,9 @@ public abstract class TabsheetBaseConnector extends return (VTabsheetBase) super.getWidget(); } + @Override + public TabsheetState getState() { + return (TabsheetState) super.getState(); + } + } diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java index 608ed1e139..b472300c21 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java @@ -17,36 +17,62 @@ package com.vaadin.client.ui.tabsheet; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; -import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.TooltipInfo; -import com.vaadin.client.UIDL; import com.vaadin.client.Util; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VTabsheet; import com.vaadin.client.ui.layout.MayScrollChildren; import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.tabsheet.TabsheetState; +import com.vaadin.shared.ui.tabsheet.TabsheetClientRpc; import com.vaadin.ui.TabSheet; @Connect(TabSheet.class) public class TabsheetConnector extends TabsheetBaseConnector implements SimpleManagedLayout, MayScrollChildren { - // Can't use "style" as it's already in use + public TabsheetConnector() { + registerRpc(TabsheetClientRpc.class, new TabsheetClientRpc() { + @Override + public void revertToSharedStateSelection() { + for (int i = 0; i < getState().tabs.size(); ++i) { + final String key = getState().tabs.get(i).key; + final boolean selected = key.equals(getState().selected); + if (selected) { + getWidget().selectTab(i); + break; + } + } + renderContent(); + } + }); + } + + @Override + protected void init() { + super.init(); + getWidget().setConnector(this); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin + * .client.communication.StateChangeEvent) + */ @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); - if (isRealUpdate(uidl)) { - // Handle stylename changes before generics (might affect size - // calculations) - getWidget().handleStyleNames(uidl, getState()); - } + getWidget().handleStyleNames(getState()); - super.updateFromUIDL(uidl, client); - if (!isRealUpdate(uidl)) { - return; + if (getState().tabsVisible) { + getWidget().showTabs(); + } else { + getWidget().hideTabs(); } // tabs; push or not @@ -76,11 +102,6 @@ public class TabsheetConnector extends TabsheetBaseConnector implements } @Override - public TabsheetState getState() { - return (TabsheetState) super.getState(); - } - - @Override public void updateCaption(ComponentConnector component) { /* Tabsheet does not render its children's captions */ } @@ -147,7 +168,20 @@ public class TabsheetConnector extends TabsheetBaseConnector implements @Override public void onConnectorHierarchyChange( - ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { - // TODO Move code from updateFromUIDL to this method + ConnectorHierarchyChangeEvent connector) { + renderContent(); + } + + /** + * (Re-)render the content of the active tab. + */ + protected void renderContent() { + ComponentConnector contentConnector = getChildComponents().get(0); + if (null != contentConnector) { + getWidget().renderContent(contentConnector.getWidget()); + } else { + getWidget().renderContent(null); + } } + } diff --git a/server/src/com/vaadin/ui/Accordion.java b/server/src/com/vaadin/ui/Accordion.java index e3c654751c..cca0a7efb8 100644 --- a/server/src/com/vaadin/ui/Accordion.java +++ b/server/src/com/vaadin/ui/Accordion.java @@ -15,6 +15,8 @@ */ package com.vaadin.ui; +import com.vaadin.shared.ui.accordion.AccordionState; + /** * An accordion is a component similar to a {@link TabSheet}, but with a * vertical orientation and the selected component presented between tabs. @@ -46,4 +48,14 @@ public class Accordion extends TabSheet { addComponents(components); } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.TabSheet#getState() + */ + @Override + protected AccordionState getState() { + return (AccordionState) super.getState(); + } + } diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java index a1f9e9dd26..8e2c40fc0f 100644 --- a/server/src/com/vaadin/ui/TabSheet.java +++ b/server/src/com/vaadin/ui/TabSheet.java @@ -27,17 +27,18 @@ import java.util.Map; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; import com.vaadin.event.FieldEvents.BlurNotifier; +import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.server.ErrorMessage; import com.vaadin.server.KeyMapper; -import com.vaadin.server.LegacyPaint; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; -import com.vaadin.shared.ui.tabsheet.TabsheetBaseConstants; -import com.vaadin.shared.ui.tabsheet.TabsheetConstants; +import com.vaadin.shared.ComponentConstants; +import com.vaadin.shared.ui.tabsheet.TabState; +import com.vaadin.shared.ui.tabsheet.TabsheetClientRpc; +import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc; +import com.vaadin.shared.ui.tabsheet.TabsheetState; import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.themes.Reindeer; import com.vaadin.ui.themes.Runo; @@ -70,7 +71,28 @@ import com.vaadin.ui.themes.Runo; * @since 3.0 */ public class TabSheet extends AbstractComponentContainer implements Focusable, - FocusNotifier, BlurNotifier, LegacyComponent, SelectiveRenderer { + FocusNotifier, BlurNotifier, SelectiveRenderer { + + /** + * Client to server RPC implementation for TabSheet. + * + * @since 7.2 + */ + protected class TabsheetServerRpcImpl implements TabsheetServerRpc { + + @Override + public void setSelected(String key) { + setSelectedTab(keyMapper.get(key)); + } + + @Override + public void closeTab(String key) { + final Component tab = keyMapper.get(key); + if (tab != null) { + closeHandler.onTabClose(TabSheet.this, tab); + } + } + } /** * List of component tabs (tab contents). In addition to being on this list, @@ -96,23 +118,20 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, private final KeyMapper<Component> keyMapper = new KeyMapper<Component>(); /** - * When true, the tab selection area is not displayed to the user. - */ - private boolean tabsHidden; - - /** * Handler to be called when a tab is closed. */ private CloseHandler closeHandler; - private int tabIndex; - /** * Constructs a new TabSheet. A TabSheet is immediate by default, and the * default close handler removes the tab being closed. */ public TabSheet() { super(); + + registerRpc(rpc); + registerRpc(focusBlurRpc); + // expand horizontally by default setWidth(100, UNITS_PERCENTAGE); setImmediate(true); @@ -167,18 +186,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * If the tab was selected, the first eligible (visible and enabled) * remaining tab is selected. * - * @param c + * @param component * the component to be removed. */ @Override - public void removeComponent(Component c) { - if (c != null && components.contains(c)) { - super.removeComponent(c); - keyMapper.remove(c); - components.remove(c); - tabs.remove(c); - if (c.equals(selected)) { + public void removeComponent(Component component) { + if (component != null && components.contains(component)) { + super.removeComponent(component); + keyMapper.remove(component); + components.remove(component); + + Tab removedTab = tabs.remove(component); + + getState().tabs + .remove(((TabSheetTabImpl) removedTab).getTabState()); + + if (component.equals(selected)) { if (components.isEmpty()) { setSelected(null); } else { @@ -308,7 +332,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * and icon and returns the corresponding (old) tab, preserving other tab * metadata like the position. * - * @param c + * @param tabComponent * the component to be added onto tab - should not be null. * @param caption * the caption to be set for the component and used rendered in @@ -334,7 +358,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * and icon and icon alternate text and returns the corresponding (old) tab, * preserving other tab metadata like the position. * - * @param c + * @param tabComponent * the component to be added onto tab - should not be null. * @param caption * the caption to be set for the component and used rendered in @@ -348,26 +372,30 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * the position at where the the tab should be added. * @return the created {@link Tab} */ - public Tab addTab(Component c, String caption, Resource icon, + public Tab addTab(Component tabComponent, String caption, Resource icon, String iconAltText, int position) { - if (c == null) { + + if (tabComponent == null) { return null; - } else if (tabs.containsKey(c)) { - Tab tab = tabs.get(c); + } else if (tabs.containsKey(tabComponent)) { + Tab tab = tabs.get(tabComponent); tab.setCaption(caption); tab.setIcon(icon, iconAltText); return tab; } else { - components.add(position, c); + components.add(position, tabComponent); + + TabSheetTabImpl tab = new TabSheetTabImpl( + keyMapper.key(tabComponent), caption, icon); - Tab tab = new TabSheetTabImpl(caption, icon); + getState().tabs.add(position, tab.getTabState()); + tabs.put(tabComponent, tab); - tabs.put(c, tab); if (selected == null) { - setSelected(c); + setSelected(tabComponent); fireSelectedTabChange(); } - super.addComponent(c); + super.addComponent(tabComponent); markAsDirty(); return tab; } @@ -393,20 +421,21 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * * If the tab sheet already contains the component, its tab is returned. * - * @param c + * @param component * the component to be added onto tab - should not be null. * @param position * The position where the tab should be added * @return the created {@link Tab} */ - public Tab addTab(Component c, int position) { - if (c == null) { - return null; - } else if (tabs.containsKey(c)) { - return tabs.get(c); - } else { - return addTab(c, c.getCaption(), c.getIcon(), position); + public Tab addTab(Component component, int position) { + Tab result = tabs.get(component); + + if (result == null) { + result = addTab(component, component.getCaption(), + component.getIcon(), position); } + + return result; } /** @@ -441,107 +470,12 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, } /** - * Paints the content of this component. - * - * @param target - * the paint target - * @throws PaintException - * if the paint operation failed. - */ - - @Override - public void paintContent(PaintTarget target) throws PaintException { - - if (areTabsHidden()) { - target.addAttribute("hidetabs", true); - } - - if (tabIndex != 0) { - target.addAttribute("tabindex", tabIndex); - } - - target.startTag("tabs"); - - for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) { - final Component component = i.next(); - - Tab tab = tabs.get(component); - - target.startTag("tab"); - if (!tab.isEnabled() && tab.isVisible()) { - target.addAttribute( - TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED, true); - } - - if (!tab.isVisible()) { - target.addAttribute("hidden", true); - } - - if (tab.isClosable()) { - target.addAttribute("closable", true); - } - - // tab icon, caption and description, but used via - // VCaption.updateCaption(uidl) - final Resource icon = tab.getIcon(); - if (icon != null) { - target.addAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON, - icon); - target.addAttribute( - TabsheetBaseConstants.ATTRIBUTE_TAB_ICON_ALT, - tab.getIconAltText()); - } - final String caption = tab.getCaption(); - if (caption != null && !caption.isEmpty()) { - target.addAttribute( - TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION, caption); - } - ErrorMessage tabError = tab.getComponentError(); - if (tabError != null) { - target.addAttribute( - TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE, - tabError.getFormattedHtmlMessage()); - } - final String description = tab.getDescription(); - if (description != null) { - target.addAttribute( - TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION, - description); - } - - final String styleName = tab.getStyleName(); - if (styleName != null && !styleName.isEmpty()) { - target.addAttribute(TabsheetConstants.TAB_STYLE_NAME, styleName); - } - - final String id = tab.getId(); - if (id != null && !id.isEmpty()) { - target.addAttribute("id", id); - } - - target.addAttribute("key", keyMapper.key(component)); - if (component.equals(selected)) { - target.addAttribute("selected", true); - LegacyPaint.paint(component, target); - } - target.endTag("tab"); - } - - target.endTag("tabs"); - - if (selected != null) { - target.addVariable(this, "selected", keyMapper.key(selected)); - } - - } - - /** * Are the tab selection parts ("tabs") hidden. * * @return true if the tabs are hidden in the UI */ public boolean areTabsHidden() { - return tabsHidden; + return !getState(false).tabsVisible; } /** @@ -551,8 +485,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * true if the tabs should be hidden */ public void hideTabs(boolean tabsHidden) { - this.tabsHidden = tabsHidden; - markAsDirty(); + getState().tabsVisible = !tabsHidden; } /** @@ -597,6 +530,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, updateSelection(); fireSelectedTabChange(); markAsDirty(); + getRpcProxy(TabsheetClientRpc.class).revertToSharedStateSelection(); } } @@ -604,22 +538,29 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * Sets the selected tab in the TabSheet. Ensures that the selected tab is * repainted if needed. * - * @param c + * @param component * The new selection or null for no selection */ - private void setSelected(Component c) { - selected = c; + private void setSelected(Component component) { + Tab tab = tabs.get(selected); + + selected = component; // Repaint of the selected component is needed as only the selected // component is communicated to the client. Otherwise this will be a // "cached" update even though the client knows nothing about the // connector if (selected != null) { - selected.markAsDirtyRecursive(); + tab = getTab(component); - Tab tab = getTab(c); if (tab != null && tab.getDefaultFocusComponent() != null) { tab.getDefaultFocusComponent().focus(); } + + getState().selected = keyMapper.key(selected); + + selected.markAsDirtyRecursive(); + } else { + getState().selected = null; } } @@ -703,27 +644,16 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, return selected; } - // inherits javadoc + private TabsheetServerRpcImpl rpc = new TabsheetServerRpcImpl(); - @Override - public void changeVariables(Object source, Map<String, Object> variables) { - if (variables.containsKey("selected")) { - setSelectedTab(keyMapper.get((String) variables.get("selected"))); - } - if (variables.containsKey("close")) { - final Component tab = keyMapper - .get((String) variables.get("close")); - if (tab != null) { - closeHandler.onTabClose(this, tab); - } - } - if (variables.containsKey(FocusEvent.EVENT_ID)) { - fireEvent(new FocusEvent(this)); - } - if (variables.containsKey(BlurEvent.EVENT_ID)) { - fireEvent(new BlurEvent(this)); + private FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl( + this) { + + @Override + protected void fireEvent(Event event) { + TabSheet.this.fireEvent(event); } - } + }; /** * Replaces a component (tab content) with another. This can be used to @@ -745,10 +675,10 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public void replaceComponent(Component oldComponent, Component newComponent) { + boolean selectAfterInserting = false; if (selected == oldComponent) { - // keep selection w/o selectedTabChange event - setSelected(newComponent); + selectAfterInserting = true; } Tab newTab = tabs.get(newComponent); @@ -758,6 +688,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, int oldLocation = -1; int newLocation = -1; int location = 0; + for (final Iterator<Component> i = components.iterator(); i.hasNext();) { final Component component = i.next(); @@ -776,6 +707,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, } else if (newLocation == -1) { removeComponent(oldComponent); newTab = addTab(newComponent, oldLocation); + + if (selectAfterInserting) { + setSelected(newComponent); + } + // Copy all relevant metadata to the new tab (#8793) // TODO Should reuse the old tab instance instead? copyTabMetadata(oldTab, newTab); @@ -783,17 +719,20 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, components.set(oldLocation, newComponent); components.set(newLocation, oldComponent); + if (selectAfterInserting) { + setSelected(newComponent); + } + // Tab associations are not changed, but metadata is swapped between // the instances // TODO Should reassociate the instances instead? - Tab tmp = new TabSheetTabImpl(null, null); + Tab tmp = new TabSheetTabImpl(null, null, null); copyTabMetadata(newTab, tmp); copyTabMetadata(oldTab, newTab); copyTabMetadata(tmp, oldTab); markAsDirty(); } - } /* Click event */ @@ -953,7 +892,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * Note! Currently only supported by TabSheet, not Accordion. * </p> * - * @param visible + * @param closable * true if the end user is allowed to close the tab, false * for not allowing to close. Should default to false. */ @@ -1148,24 +1087,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, */ public class TabSheetTabImpl implements Tab { - private String caption = ""; - private Resource icon = null; - private boolean enabled = true; - private boolean visible = true; - private boolean closable = false; - private String description = null; - private ErrorMessage componentError = null; - private String styleName; - private String id; - private String iconAltText = ""; + private TabState tabState; + private Focusable defaultFocus; - public TabSheetTabImpl(String caption, Resource icon) { + private ErrorMessage componentError; + + public TabSheetTabImpl(String key, String caption, Resource icon) { + tabState = new TabState(); + if (caption == null) { caption = ""; } - this.caption = caption; - this.icon = icon; + + tabState.key = key; + tabState.caption = caption; + + setIcon(icon); } /** @@ -1174,40 +1112,36 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public String getCaption() { - return caption; + return tabState.caption; } @Override public void setCaption(String caption) { - this.caption = caption; + tabState.caption = caption; markAsDirty(); } @Override public Resource getIcon() { - return icon; + return getResource(ComponentConstants.ICON_RESOURCE + + tabState.key); } @Override public void setIcon(Resource icon) { + // this might not be ideal (resetting icon altText), but matches + // previous semantics setIcon(icon, ""); } @Override - public void setIcon(Resource icon, String iconAltText) { - this.icon = icon; - this.iconAltText = iconAltText; - markAsDirty(); - } - - @Override public String getIconAltText() { - return iconAltText; + return tabState.iconAltText; } @Override public void setIconAltText(String iconAltText) { - this.iconAltText = iconAltText; + tabState.iconAltText = iconAltText; markAsDirty(); } @@ -1223,12 +1157,13 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public boolean isEnabled() { - return enabled; + return tabState.enabled; } @Override public void setEnabled(boolean enabled) { - this.enabled = enabled; + tabState.enabled = enabled; + if (updateSelection()) { fireSelectedTabChange(); } @@ -1237,12 +1172,13 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public boolean isVisible() { - return visible; + return tabState.visible; } @Override public void setVisible(boolean visible) { - this.visible = visible; + tabState.visible = visible; + if (updateSelection()) { fireSelectedTabChange(); } @@ -1251,27 +1187,24 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public boolean isClosable() { - return closable; + return tabState.closable; } @Override public void setClosable(boolean closable) { - this.closable = closable; - markAsDirty(); - } - - public void close() { + tabState.closable = closable; + markAsDirty(); } @Override public String getDescription() { - return description; + return tabState.description; } @Override public void setDescription(String description) { - this.description = description; + tabState.description = description; markAsDirty(); } @@ -1283,6 +1216,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public void setComponentError(ErrorMessage componentError) { this.componentError = componentError; + + String formattedHtmlMessage = componentError != null ? componentError + .getFormattedHtmlMessage() : null; + tabState.componentError = formattedHtmlMessage; + markAsDirty(); } @@ -1298,25 +1236,37 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public void setStyleName(String styleName) { - this.styleName = styleName; + tabState.styleName = styleName; + markAsDirty(); } @Override public String getStyleName() { - return styleName; + return tabState.styleName; + } + + protected TabState getTabState() { + return tabState; } @Override public void setId(String id) { - this.id = id; + tabState.id = id; markAsDirty(); } @Override public String getId() { - return id; + return tabState.id; + } + + @Override + public void setIcon(Resource icon, String iconAltText) { + setResource(ComponentConstants.ICON_RESOURCE + tabState.key, + icon); + tabState.iconAltText = iconAltText; } } @@ -1371,7 +1321,9 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, int oldPosition = getTabPosition(tab); components.remove(oldPosition); components.add(position, tab.getComponent()); - markAsDirty(); + + getState().tabs.remove(oldPosition); + getState().tabs.add(position, ((TabSheetTabImpl) tab).getTabState()); } /** @@ -1392,13 +1344,12 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, @Override public int getTabIndex() { - return tabIndex; + return getState(false).tabIndex; } @Override public void setTabIndex(int tabIndex) { - this.tabIndex = tabIndex; - markAsDirty(); + getState().tabIndex = tabIndex; } @Override @@ -1485,4 +1436,14 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, to.setStyleName(from.getStyleName()); to.setComponentError(from.getComponentError()); } + + @Override + protected TabsheetState getState(boolean markAsDirty) { + return (TabsheetState) super.getState(markAsDirty); + } + + @Override + protected TabsheetState getState() { + return (TabsheetState) super.getState(); + } } diff --git a/shared/src/com/vaadin/shared/ui/accordion/AccordionState.java b/shared/src/com/vaadin/shared/ui/accordion/AccordionState.java new file mode 100644 index 0000000000..67b20fc040 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/accordion/AccordionState.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.accordion; + +import com.vaadin.shared.ui.tabsheet.TabsheetState; + +public class AccordionState extends TabsheetState { + + public static final String PRIMARY_STYLE_NAME = "v-accordion"; + + { + primaryStyleName = PRIMARY_STYLE_NAME; + } + +} diff --git a/shared/src/com/vaadin/shared/ui/tabsheet/TabState.java b/shared/src/com/vaadin/shared/ui/tabsheet/TabState.java new file mode 100644 index 0000000000..9620a61c9a --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/tabsheet/TabState.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.tabsheet; + +import java.io.Serializable; + +/** + * Shared state of a single tab in a Tabsheet or an Accordion. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class TabState implements Serializable { + + public String caption = ""; + public boolean enabled = true; + public boolean visible = true; + public boolean closable = false; + public String description = null; + public String styleName; + public String key; + public String componentError; + public String id; + public String iconAltText; + +}
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetClientRpc.java b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetClientRpc.java new file mode 100644 index 0000000000..780e0f1ac5 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetClientRpc.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.tabsheet; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * Server to client RPC methods for the TabSheet. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface TabsheetClientRpc extends ClientRpc { + + /** + * Forces the client to switch to the tab that is selected by the server. + * + * This is required e.g. for reverting tab selection change on the server + * side (shared state does not change). + */ + public void revertToSharedStateSelection(); +} diff --git a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetServerRpc.java b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetServerRpc.java new file mode 100644 index 0000000000..996525151c --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetServerRpc.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.tabsheet; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * Client to server RPC methods for the TabSheet. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface TabsheetServerRpc extends ServerRpc { + + /** + * Tell server that a tab has been selected by the user. + * + * @param key + * internal key of the tab + */ + void setSelected(String key); + + /** + * Tell server that a tab has been closed by the user. + * + * @param key + * internal key of the tab + */ + void closeTab(String key); + +} diff --git a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetState.java b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetState.java index dbb91b8b3d..77e9e15400 100644 --- a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetState.java +++ b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetState.java @@ -15,10 +15,29 @@ */ package com.vaadin.shared.ui.tabsheet; +import java.util.ArrayList; + import com.vaadin.shared.AbstractComponentState; public class TabsheetState extends AbstractComponentState { + public static final String PRIMARY_STYLE_NAME = "v-tabsheet"; + { - primaryStyleName = "v-tabsheet"; + primaryStyleName = PRIMARY_STYLE_NAME; } + + /** + * Index of the component when switching focus - not related to Tabsheet + * tabs. + */ + public int tabIndex; + + public ArrayList<TabState> tabs = new ArrayList<TabState>(); + + /** true to show the tab bar, false to only show the contained component */ + public boolean tabsVisible = true; + + /** the key of the currently selected tab */ + public String selected; + } diff --git a/uitest/src/com/vaadin/tests/components/accordion/RemoveTabs.java b/uitest/src/com/vaadin/tests/components/accordion/RemoveTabs.java index d24def22dd..e82336c1e8 100644 --- a/uitest/src/com/vaadin/tests/components/accordion/RemoveTabs.java +++ b/uitest/src/com/vaadin/tests/components/accordion/RemoveTabs.java @@ -46,38 +46,34 @@ public class RemoveTabs extends TestBase { getLayout().addComponent(accordion); closeCurrent = new Button("Close current tab"); - closeCurrent.addListener(new Button.ClickListener() { + closeCurrent.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { closeCurrentTab(); - } }); closeFirst = new Button("close first tab"); - closeFirst.addListener(new Button.ClickListener() { + closeFirst.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { closeFirstTab(); - } }); closeLast = new Button("close last tab"); - closeLast.addListener(new Button.ClickListener() { + closeLast.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { closeLastTab(); - } }); reorderTabs = new Button("reorder"); - reorderTabs.addListener(new Button.ClickListener() { + reorderTabs.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { reorder(); - } }); @@ -96,11 +92,11 @@ public class RemoveTabs extends TestBase { } private void closeFirstTab() { - accordion.removeComponent(accordion.getComponentIterator().next()); + accordion.removeComponent(accordion.iterator().next()); } private void closeLastTab() { - Iterator<Component> i = accordion.getComponentIterator(); + Iterator<Component> i = accordion.iterator(); Component last = null; while (i.hasNext()) { last = i.next(); @@ -114,7 +110,7 @@ public class RemoveTabs extends TestBase { if (container != null) { List<Component> c = new ArrayList<Component>(); - Iterator<Component> i = container.getComponentIterator(); + Iterator<Component> i = container.iterator(); while (i.hasNext()) { Component comp = i.next(); c.add(comp); @@ -124,7 +120,6 @@ public class RemoveTabs extends TestBase { for (int j = c.size() - 1; j >= 0; j--) { container.addComponent(c.get(j)); } - } } diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheet.java b/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheet.java new file mode 100644 index 0000000000..3cbe624ee0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheet.java @@ -0,0 +1,27 @@ +package com.vaadin.tests.components.tabsheet; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.TabSheet; + +public class EmptyTabSheet extends TestBase { + + private TabSheet tabSheet; + + @Override + protected String getDescription() { + return "Test a TabSheet without any tabs."; + } + + @Override + protected Integer getTicketNumber() { + return null; + } + + @Override + protected void setup() { + tabSheet = new TabSheet(); + tabSheet.setId("tabsheet"); + addComponent(tabSheet); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheetTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheetTest.java new file mode 100644 index 0000000000..dabc9c8e0b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/EmptyTabSheetTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.tabsheet; + +import org.junit.Test; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class EmptyTabSheetTest extends MultiBrowserTest { + @Test + public void emptyTabSheet() throws Exception { + openTestURL(); + + compareScreen("empty"); + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.html b/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.html deleted file mode 100644 index c4f5998f47..0000000000 --- a/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.html +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="http://localhost:8080/" /> -<title>PreventTabChange</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">PreventTabChange</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.tabsheet.PreventTabChange</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentstabsheetPreventTabChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentstabsheetPreventTabChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td></td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentstabsheetPreventTabChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentstabsheetPreventTabChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td></td> -</tr> - -</tbody></table> -</body> -</html> diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.java b/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.java index 434c73f778..fddb036728 100644 --- a/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.java +++ b/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChange.java @@ -31,7 +31,8 @@ public class PreventTabChange extends TestBase implements @Override protected void setup() { tabSheet = new TabSheet(); - tabSheet.addListener(this); + tabSheet.setId("tabsheet"); + tabSheet.addSelectedTabChangeListener(this); tab1 = new Label("Tab 1 contents"); tab2 = new Label("Tab 2 contents"); tab3 = new Label("Tab 3 contents"); @@ -48,8 +49,8 @@ public class PreventTabChange extends TestBase implements @Override public void selectedTabChange(SelectedTabChangeEvent event) { - TabSheet tabsheet = event.getTabSheet(); + if (lastTab == tab1) { if (tabsheet.getSelectedTab() != tab2) { tabsheet.setSelectedTab(lastTab); @@ -63,6 +64,7 @@ public class PreventTabChange extends TestBase implements tabsheet.setSelectedTab(lastTab); } } + lastTab = tabsheet.getSelectedTab(); } } diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChangeTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChangeTest.java new file mode 100644 index 0000000000..31a59aaed6 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/PreventTabChangeTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.tabsheet; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class PreventTabChangeTest extends MultiBrowserTest { + @Test + public void preventTabChange() throws Exception { + openTestURL(); + + clickTab(1); + clickTab(2); + Thread.sleep(2000); + assertTabSelected(2); + Assert.assertEquals("Tab 3 contents", getSelectedTabContent().getText()); + clickTab(0); + clickTab(2); + assertTabSelected(0); + Assert.assertEquals("Tab 1 contents", getSelectedTabContent().getText()); + } + + private void assertTabSelected(int i) throws NoSuchElementException { + WebElement tabItem = findTab(i).findElement(By.xpath("..")); + Assert.assertTrue("Tab " + i + " should be selected but isn't", tabItem + .getAttribute("class").contains("v-tabsheet-tabitem-selected")); + } + + private void clickTab(int i) { + findTab(i).click(); + } + + private WebElement findTab(int i) { + return driver.findElement(com.vaadin.testbench.By + .vaadin("//TabSheet#tab[" + i + "]")); + } + + private WebElement getSelectedTabContent() { + return driver.findElement(com.vaadin.testbench.By + .vaadin("//TabSheet#tabpanel")); + } + +}
\ No newline at end of file |