From e0220a1c1a15f59f92647379cbc3fcd79a077f81 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Thu, 18 Aug 2011 10:36:01 +0000 Subject: [PATCH] #5738 TabSheet's first visible tab should be rendered as if it is the first tab - merged refactoring written by Artur & added test for the first tab #5880 Updated to use the new api svn changeset:20466/svn branch:6.7 --- .../terminal/gwt/client/ui/VTabsheet.java | 528 +++++++++++------- .../components/tabsheet/AddAndRemoveTabs.html | 13 +- 2 files changed, 343 insertions(+), 198 deletions(-) diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java index 6188305436..016a411447 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java @@ -4,10 +4,7 @@ package com.vaadin.terminal.gwt.client.ui; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Set; import com.google.gwt.core.client.Scheduler; @@ -22,6 +19,7 @@ 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.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; @@ -35,14 +33,162 @@ import com.vaadin.terminal.gwt.client.VCaption; public class VTabsheet extends VTabsheetBase { - private class TabSheetCaption extends VCaption { + private static class VCloseEvent { + private Tab tab; + + VCloseEvent(Tab tab) { + this.tab = tab; + } + + public Tab getTab() { + return tab; + } + + } + + private interface VCloseHandler { + public void onClose(VCloseEvent event); + } + + /** + * Representation of a single "tab" shown in the TabBar + * + */ + private static class Tab extends SimplePanel { + private static final String TD_CLASSNAME = CLASSNAME + "-tabitemcell"; + private static final String TD_FIRST_CLASSNAME = TD_CLASSNAME + + "-first"; + private static final String TD_SELECTED_CLASSNAME = TD_CLASSNAME + + "-selected"; + private static final String TD_SELECTED_FIRST_CLASSNAME = TD_SELECTED_CLASSNAME + + "-first"; + + private static final String DIV_CLASSNAME = CLASSNAME + "-tabitem"; + private static final String DIV_SELECTED_CLASSNAME = DIV_CLASSNAME + + "-selected"; + + private TabCaption tabCaption; + Element td = getElement(); + private VCloseHandler closeHandler; + + private boolean enabledOnServer = true; + private Element div; + private TabBar tabBar; + private boolean hiddenOnServer = false; + + private String styleName; + + private Tab(TabBar tabBar) { + super(DOM.createTD()); + this.tabBar = tabBar; + setStyleName(td, TD_CLASSNAME); + + div = DOM.createDiv(); + setStyleName(div, DIV_CLASSNAME); + + DOM.appendChild(td, div); + + tabCaption = new TabCaption(this, getTabsheet() + .getApplicationConnection()); + add(tabCaption); + + } + + public boolean isHiddenOnServer() { + return hiddenOnServer; + } + + public void setHiddenOnServer(boolean hiddenOnServer) { + this.hiddenOnServer = hiddenOnServer; + } + + @Override + protected Element getContainerElement() { + // Attach caption element to div, not td + return div; + } + + public boolean isEnabledOnServer() { + return enabledOnServer; + } + + public void setEnabledOnServer(boolean enabled) { + enabledOnServer = enabled; + } + + public void addClickHandler(ClickHandler handler) { + tabCaption.addClickHandler(handler); + } + + public void setCloseHandler(VCloseHandler closeHandler) { + this.closeHandler = closeHandler; + } + + /** + * Toggles the style names for the Tab + * + * @param selected + * true if the Tab is selected + * @param first + * true if the Tab is the first visible Tab + */ + public void setStyleNames(boolean selected, boolean first) { + setStyleName(td, TD_FIRST_CLASSNAME, first); + setStyleName(td, TD_SELECTED_CLASSNAME, selected); + setStyleName(td, TD_SELECTED_FIRST_CLASSNAME, selected && first); + setStyleName(div, DIV_SELECTED_CLASSNAME, selected); + } + + public void onClose() { + closeHandler.onClose(new VCloseEvent(this)); + } + + public VTabsheet getTabsheet() { + return tabBar.getTabsheet(); + } + + public void updateFromUIDL(UIDL tabUidl) { + tabCaption.updateCaption(tabUidl); + + // Apply the styleName set for the tab + String newStyleName = tabUidl.getStringAttribute(TAB_STYLE_NAME); + // Find the nth td element + if (newStyleName != null && newStyleName.length() != 0) { + if (!newStyleName.equals(styleName)) { + // If we have a new style name + if (styleName != null && styleName.length() != 0) { + // Remove old style name if present + td.removeClassName(TD_CLASSNAME + "-" + styleName); + } + // Set new style name + td.addClassName(TD_CLASSNAME + "-" + newStyleName); + styleName = newStyleName; + } + } else if (styleName != null) { + // Remove the set stylename if no stylename is present in the + // uidl + td.removeClassName(TD_CLASSNAME + "-" + styleName); + styleName = null; + } + } + + public void recalculateCaptionWidth() { + tabCaption.setWidth(tabCaption.getRequiredWidth() + "px"); + } + + } + + private static class TabCaption extends VCaption { - private boolean hidden = false; private boolean closable = false; private Element closeButton; + private Tab tab; + private ApplicationConnection client; - TabSheetCaption() { + TabCaption(Tab tab, ApplicationConnection client) { super(null, client); + this.client = client; + this.tab = tab; } @Override @@ -55,10 +201,9 @@ public class VTabsheet extends VTabsheetBase { if (uidl.hasAttribute(ATTRIBUTE_ERROR)) { tooltipInfo.setErrorUidl(uidl.getErrors()); } - client.registerTooltip(VTabsheet.this, getElement(), - tooltipInfo); + client.registerTooltip(getTabsheet(), getElement(), tooltipInfo); } else { - client.registerTooltip(VTabsheet.this, getElement(), null); + client.registerTooltip(getTabsheet(), getElement(), null); } boolean ret = super.updateCaption(uidl); @@ -68,30 +213,29 @@ public class VTabsheet extends VTabsheetBase { return ret; } + private VTabsheet getTabsheet() { + return tab.getTabsheet(); + } + @Override public void onBrowserEvent(Event event) { if (closable && event.getTypeInt() == Event.ONCLICK && event.getEventTarget().cast() == closeButton) { - final String tabKey = tabKeys.get(tb.getTabIndex(this)) - .toString(); - if (!disabledTabKeys.contains(tabKey)) { - client.updateVariable(id, "close", tabKey, true); - event.stopPropagation(); - event.preventDefault(); - return; - } + tab.onClose(); + event.stopPropagation(); + event.preventDefault(); } super.onBrowserEvent(event); if (event.getTypeInt() == Event.ONLOAD) { - // icon onloads may change total width of tabsheet - if (isDynamicWidth()) { - updateDynamicWidth(); - } - updateTabScroller(); + getTabsheet().tabSizeMightHaveChanged(getTab()); } - client.handleTooltipEvent(event, VTabsheet.this, getElement()); + client.handleTooltipEvent(event, getTabsheet(), getElement()); + } + + public Tab getTab() { + return tab; } @Override @@ -121,14 +265,6 @@ public class VTabsheet extends VTabsheetBase { captionText.getStyle().setPropertyPx("width", captionWidth); } - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - public void setClosable(boolean closable) { this.closable = closable; if (closable && closeButton == null) { @@ -160,13 +296,20 @@ public class VTabsheet extends VTabsheetBase { } - class TabBar extends ComplexPanel implements ClickHandler { + static class TabBar extends ComplexPanel implements ClickHandler, + VCloseHandler { private final Element tr = DOM.createTR(); private final Element spacerTd = DOM.createTD(); - TabBar() { + private Tab selected; + + private VTabsheet tabsheet; + + TabBar(VTabsheet tabsheet) { + this.tabsheet = tabsheet; + Element el = DOM.createTable(); Element tbody = DOM.createTBody(); DOM.appendChild(el, tbody); @@ -177,128 +320,173 @@ public class VTabsheet extends VTabsheetBase { setElement(el); } + public void onClose(VCloseEvent event) { + Tab tab = event.getTab(); + if (!tab.isEnabledOnServer()) { + return; + } + int tabIndex = getWidgetIndex(tab); + getTabsheet().sendTabClosedEvent(tabIndex); + } + protected Element getContainerElement() { return tr; } - private Widget oldSelected; - public int getTabCount() { return getWidgetCount(); } - public void addTab(VCaption c) { - Element td = DOM.createTD(); - setStyleName(td, ITEMCELL_CLASSNAME); - tabStyles.add(null); + public Tab addTab() { + Tab t = new Tab(this); + + // Logical attach + int spacerIndex = getTabCount(); + insert(t, tr, spacerIndex, true); - if (getWidgetCount() == 0) { - setStyleName(td, CLASSNAME + "-tabitemcell-first", true); + if (getTabCount() == 0) { + // Set the "first" style + t.setStyleNames(false, true); } - Element div = DOM.createDiv(); - setStyleName(div, CLASSNAME + "-tabitem"); - DOM.appendChild(td, div); - DOM.insertBefore(tr, td, spacerTd); - c.addClickHandler(this); - add(c, div); + t.addClickHandler(this); + t.setCloseHandler(this); + + return t; } public void onClick(ClickEvent event) { - int index = getWidgetIndex((Widget) event.getSource()); - onTabSelected(index); + Widget caption = (Widget) event.getSource(); + int index = getWidgetIndex(caption.getParent()); + getTabsheet().onTabSelected(index); } - public void selectTab(int index) { - final String classname = CLASSNAME + "-tabitem-selected"; - String classname2 = CLASSNAME + "-tabitemcell-selected" - + (index == 0 ? "-first" : ""); + public VTabsheet getTabsheet() { + return tabsheet; + } - final Widget newSelected = getWidget(index); - final com.google.gwt.dom.client.Element div = newSelected - .getElement().getParentElement(); + public Tab getTab(int index) { + if (index < 0 || index >= getTabCount()) { + return null; + } + return (Tab) super.getWidget(index); + } + + public void selectTab(int index) { + final Tab newSelected = getTab(index); + final Tab oldSelected = selected; - Widget.setStyleName(div, classname, true); - Widget.setStyleName(div.getParentElement(), classname2, true); + newSelected.setStyleNames(true, isFirstVisibleTab(index)); if (oldSelected != null && oldSelected != newSelected) { - classname2 = CLASSNAME + "-tabitemcell-selected" - + (getWidgetIndex(oldSelected) == 0 ? "-first" : ""); - final com.google.gwt.dom.client.Element divOld = oldSelected - .getElement().getParentElement(); - Widget.setStyleName(divOld, classname, false); - Widget.setStyleName(divOld.getParentElement(), classname2, - false); + oldSelected.setStyleNames(false, + isFirstVisibleTab(getWidgetIndex(oldSelected))); } - oldSelected = newSelected; + + // Update the field holding the currently selected tab + selected = newSelected; // The selected tab might need more (or less) space - updateCaptionSize(index); - updateCaptionSize(activeTabIndex); + newSelected.recalculateCaptionWidth(); + getTab(tabsheet.activeTabIndex).recalculateCaptionWidth(); } public void removeTab(int i) { - Widget w = getWidget(i); - if (w == null) { + Tab tab = getTab(i); + if (tab == null) { return; } - Element caption = w.getElement(); - Element div = DOM.getParent(caption); - Element td = DOM.getParent(div); - Element tr = DOM.getParent(td); - remove(w); - - /* - * Widget is the Caption but we want to remove everything up to and - * including the parent TD - */ - - DOM.removeChild(tr, td); + remove(tab); /* * If this widget was selected we need to unmark it as the last * selected */ - if (w == oldSelected) { - oldSelected = null; + if (tab == selected) { + selected = null; } + + // FIXME: Shouldn't something be selected instead? } - public TabSheetCaption getTab(int index) { - if (index >= getWidgetCount()) { - return null; - } - return (TabSheetCaption) getWidget(index); + private boolean isFirstVisibleTab(int index) { + return getFirstVisibleTab() == index; } - public int getTabIndex(TabSheetCaption tab) { - return getChildren().indexOf(tab); + /** + * Returns the index of the first visible tab + * + * @return + */ + private int getFirstVisibleTab() { + return getNextVisibleTab(-1); } - public void setVisible(int index, boolean visible) { - com.google.gwt.dom.client.Element e = getTab(index).getElement() - .getParentElement().getParentElement(); - if (visible) { - e.getStyle().setProperty("display", ""); + /** + * Find the next visible tab. Returns -1 if none is found. + * + * @param i + * @return + */ + private int getNextVisibleTab(int i) { + int tabs = getTabCount(); + do { + i++; + } while (i < tabs && getTab(i).isHiddenOnServer()); + + if (i == tabs) { + return -1; } else { - e.getStyle().setProperty("display", "none"); + return i; } } - public void updateCaptionSize(int index) { - VCaption c = getTab(index); - c.setWidth(c.getRequiredWidth() + "px"); + /** + * Find the previous visible tab. Returns -1 if none is found. + * + * @param i + * @return + */ + private int getPreviousVisibleTab(int i) { + do { + i--; + } while (i >= 0 && getTab(i).isHiddenOnServer()); + + return i; } + public int scrollLeft(int currentFirstVisible) { + int prevVisible = getPreviousVisibleTab(currentFirstVisible); + if (prevVisible == -1) { + return -1; + } + + Tab newFirst = getTab(prevVisible); + newFirst.setVisible(true); + newFirst.recalculateCaptionWidth(); + + return prevVisible; + } + + public int scrollRight(int currentFirstVisible) { + int nextVisible = getNextVisibleTab(currentFirstVisible); + if (nextVisible == -1) { + return -1; + } + Tab currentFirst = getTab(currentFirstVisible); + currentFirst.setVisible(false); + currentFirst.recalculateCaptionWidth(); + return nextVisible; + } + } public static final String CLASSNAME = "v-tabsheet"; public static final String TABS_CLASSNAME = "v-tabsheet-tabcontainer"; public static final String SCROLLER_CLASSNAME = "v-tabsheet-scroller"; - public static final String ITEMCELL_CLASSNAME = CLASSNAME + "-tabitemcell"; // Can't use "style" as it's already in use public static final String TAB_STYLE_NAME = "tabstyle"; @@ -313,12 +501,10 @@ public class VTabsheet extends VTabsheetBase { */ private int scrollerIndex = 0; - private final TabBar tb = new TabBar(); + private final TabBar tb = new TabBar(this); private final VTabsheetPanel tp = new VTabsheetPanel(); private final Element contentNode, deco; - private final HashMap captions = new HashMap(); - private String height; private String width; @@ -337,12 +523,6 @@ public class VTabsheet extends VTabsheetBase { private String currentStyle; - /** - * Keeps track of the currently set styleName for each tab so it can be - * removed without affecting the other styles set to the same DOM element - */ - private List tabStyles = new ArrayList(); - private void onTabSelected(final int tabIndex) { if (disabled || waitingForResponse) { return; @@ -370,6 +550,23 @@ public class VTabsheet extends VTabsheetBase { } } + public ApplicationConnection getApplicationConnection() { + return client; + } + + public void tabSizeMightHaveChanged(Tab tab) { + // icon onloads may change total width of tabsheet + if (isDynamicWidth()) { + updateDynamicWidth(); + } + updateTabScroller(); + + } + + void sendTabClosedEvent(int tabIndex) { + client.updateVariable(id, "close", tabKeys.get(tabIndex), true); + } + private boolean isDynamicWidth() { return width == null || width.equals(""); } @@ -430,20 +627,16 @@ public class VTabsheet extends VTabsheetBase { // Tab scrolling if (isScrolledTabs() && DOM.eventGetTarget(event) == scrollerPrev) { - int prevVisible = getPreviousVisibleTab(scrollerIndex); - if (prevVisible != -1) { - tb.setVisible(prevVisible, true); - tb.updateCaptionSize(prevVisible); - scrollerIndex = prevVisible; + int newFirstIndex = tb.scrollLeft(scrollerIndex); + if (newFirstIndex != -1) { + scrollerIndex = newFirstIndex; updateTabScroller(); } } else if (isClippedTabs() && DOM.eventGetTarget(event) == scrollerNext) { - int firstVisible = scrollerIndex; - int nextVisible = getNextVisibleTab(firstVisible); - if (nextVisible != -1) { - tb.setVisible(firstVisible, false); - tb.updateCaptionSize(firstVisible); - scrollerIndex = nextVisible; + int newFirstIndex = tb.scrollRight(scrollerIndex); + + if (newFirstIndex != -1) { + scrollerIndex = newFirstIndex; updateTabScroller(); } } else { @@ -451,40 +644,6 @@ public class VTabsheet extends VTabsheetBase { } } - /** - * Find the next visible tab. Returns -1 if none is found. - * - * @param i - * @return - */ - private int getNextVisibleTab(int i) { - int tabs = tb.getTabCount(); - do { - i++; - } while (i < tabs && tb.getTab(i).isHidden()); - - if (i == tabs) { - return -1; - } else { - return i; - } - } - - /** - * Find the previous visible tab. Returns -1 if none is found. - * - * @param i - * @return - */ - private int getPreviousVisibleTab(int i) { - do { - i--; - } while (i >= 0 && tb.getTab(i).isHidden()); - - return i; - - } - /** * Checks if the tab with the selected index has been scrolled out of the * view (on the left side). @@ -646,51 +805,26 @@ public class VTabsheet extends VTabsheetBase { @Override protected void renderTab(final UIDL tabUidl, int index, boolean selected, boolean hidden) { - TabSheetCaption c = tb.getTab(index); - if (c == null) { - c = new TabSheetCaption(); - tb.addTab(c); - } - c.updateCaption(tabUidl); - - // Apply the styleName set for the tab - String styleName = tabUidl.getStringAttribute(TAB_STYLE_NAME); - String oldStyleName = tabStyles.get(index); - // Find the nth td element - Element td = (Element) tb.tr.getChild(index); - if (styleName != null && styleName.length() != 0) { - if (!styleName.equals(oldStyleName)) { - // If we have a new style name - if (oldStyleName != null && oldStyleName.length() != 0) { - // Remove old style name if present - td.removeClassName(ITEMCELL_CLASSNAME + "-" + oldStyleName); - } - // Set new style name - td.addClassName(ITEMCELL_CLASSNAME + "-" + styleName); - // Update bookkeeping - tabStyles.set(index, styleName); - } - } else if (oldStyleName != null) { - // Remove the set stylename if no stylename is present in the uidl - td.removeClassName(ITEMCELL_CLASSNAME + "-" + oldStyleName); - // Also update the bookkeeping - tabStyles.set(index, null); + Tab tab = tb.getTab(index); + if (tab == null) { + tab = tb.addTab(); } + tab.updateFromUIDL(tabUidl); + tab.setEnabledOnServer((!disabledTabKeys.contains(tabKeys.get(index)))); + tab.setHiddenOnServer(hidden); - c.setHidden(hidden); if (scrolledOutOfView(index)) { // Should not set tabs visible if they are scrolled out of view hidden = true; } // Set the current visibility of the tab (in the browser) - tb.setVisible(index, !hidden); + tab.setVisible(!hidden); /* * Force the width of the caption container so the content will not wrap * and tabs won't be too narrow in certain browsers */ - c.setWidth(c.getRequiredWidth() + "px"); - captions.put("" + index, c); + tab.recalculateCaptionWidth(); UIDL tabContentUIDL = null; Paintable tabContent = null; @@ -910,9 +1044,10 @@ public class VTabsheet extends VTabsheetBase { // Make sure scrollerIndex is valid if (scrollerIndex < 0 || scrollerIndex > tb.getTabCount()) { - scrollerIndex = getNextVisibleTab(-1); - } else if (tb.getTabCount() > 0 && tb.getTab(scrollerIndex).isHidden()) { - scrollerIndex = getNextVisibleTab(scrollerIndex); + scrollerIndex = tb.getFirstVisibleTab(); + } else if (tb.getTabCount() > 0 + && tb.getTab(scrollerIndex).isHiddenOnServer()) { + scrollerIndex = tb.getNextVisibleTab(scrollerIndex); } boolean scrolled = isScrolledTabs(); @@ -950,16 +1085,17 @@ public class VTabsheet extends VTabsheetBase { } private void showAllTabs() { - scrollerIndex = getNextVisibleTab(-1); + scrollerIndex = tb.getFirstVisibleTab(); for (int i = 0; i < tb.getTabCount(); i++) { - if (!tb.getTab(i).isHidden()) { - tb.setVisible(i, true); + Tab t = tb.getTab(i); + if (!t.isHiddenOnServer()) { + t.setVisible(true); } } } private boolean isScrolledTabs() { - return scrollerIndex > getNextVisibleTab(-1); + return scrollerIndex > tb.getFirstVisibleTab(); } private boolean isClippedTabs() { @@ -1052,7 +1188,7 @@ public class VTabsheet extends VTabsheetBase { @Override protected int getTabCount() { - return tb.getWidgetCount(); + return tb.getTabCount(); } @Override diff --git a/tests/src/com/vaadin/tests/components/tabsheet/AddAndRemoveTabs.html b/tests/src/com/vaadin/tests/components/tabsheet/AddAndRemoveTabs.html index e919454fc2..993ef79c91 100644 --- a/tests/src/com/vaadin/tests/components/tabsheet/AddAndRemoveTabs.html +++ b/tests/src/com/vaadin/tests/components/tabsheet/AddAndRemoveTabs.html @@ -13,7 +13,7 @@ open - /run/com.vaadin.tests.components.tabsheet.AddAndRemoveTabs + /run/com.vaadin.tests.components.tabsheet.AddAndRemoveTabs?restartApplication @@ -111,7 +111,16 @@ - + + click + vaadin=runcomvaadintestscomponentstabsheetAddAndRemoveTabs::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/VTabsheetPanel[0]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0] + + + + assertCSSClass + vaadin=runcomvaadintestscomponentstabsheetAddAndRemoveTabs::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0] + v-tabsheet-tabitemcell-first + -- 2.39.5