package com.itmill.toolkit.terminal.gwt.client.ui;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public static final String CLASSNAME = "i-accordion";
- private ArrayList<StackItem> stack = new ArrayList<StackItem>();
-
private Set<Paintable> paintables = new HashSet<Paintable>();
private String height;
* the content area is
*/
if (selectedUIDLItemIndex >= 0) {
- StackItem selectedItem = stack.get(selectedUIDLItemIndex);
+ StackItem selectedItem = getStackItem(selectedUIDLItemIndex);
UIDL selectedTabUIDL = lazyUpdateMap.remove(selectedItem);
open(selectedUIDLItemIndex);
boolean hidden) {
StackItem item;
int itemIndex;
- if (stack.size() <= index) {
+ if (getWidgetCount() <= index) {
// Create stackItem and render caption
item = new StackItem(tabUidl);
- if (stack.size() == 0) {
+ if (getWidgetCount() == 0) {
item.addStyleDependentName("first");
}
- stack.add(item);
- itemIndex = stack.size() - 1;
+ itemIndex = getWidgetCount();
add(item, getElement());
} else {
- item = stack.get(index);
+ item = getStackItem(index);
+ item = moveStackItemIfNeeded(item, index, tabUidl);
itemIndex = index;
item.updateCaption(tabUidl);
}
}
}
+ /**
+ * 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;
+ Paintable tabContent = null;
+ if (tabUidl.getChildCount() > 0) {
+ tabContentUIDL = tabUidl.getChildUIDL(0);
+ tabContent = client.getPaintable(tabContentUIDL);
+ }
+
+ Widget itemWidget = item.getComponent();
+ if (tabContent != null) {
+ if (tabContent != itemWidget) {
+ /*
+ * This is not the same widget as before, find out if it has
+ * been moved
+ */
+ int oldIndex = -1;
+ StackItem oldItem = null;
+ for (int i = 0; i < getWidgetCount(); i++) {
+ Widget w = getWidget(i);
+ oldItem = (StackItem) w;
+ if (tabContent == oldItem.getComponent()) {
+ oldIndex = i;
+ break;
+ }
+ }
+
+ if (oldIndex != -1 && oldIndex > newIndex) {
+ /*
+ * The tab has previously been rendered in another position
+ * so we must move the cached content to correct position.
+ * We move only items with oldIndex > newIndex to prevent
+ * moving items already rendered in this update. If for
+ * instance tabs 1,2,3 are removed and added as 3,2,1 we
+ * cannot re-use "1" when we get to the third tab.
+ */
+ insert(oldItem, getElement(), newIndex, true);
+ return oldItem;
+ }
+ }
+ } else {
+ // Tab which has never been loaded. Must assure we use an empty
+ // StackItem
+ Widget oldWidget = item.getComponent();
+ if (oldWidget != null) {
+ item = new StackItem(tabUidl);
+ insert(item, getElement(), newIndex, true);
+ }
+ }
+ return item;
+ }
+
private void open(int itemIndex) {
- StackItem item = stack.get(itemIndex);
+ StackItem item = (StackItem) getWidget(itemIndex);
boolean alreadyOpen = false;
if (openTab != null) {
if (openTab.isOpen()) {
@Override
protected void selectTab(final int index, final UIDL contentUidl) {
- StackItem item = stack.get(index);
+ StackItem item = getStackItem(index);
if (index != activeTabIndex) {
open(index);
iLayout();
}
public void onSelectTab(StackItem item) {
- final int index = stack.indexOf(item);
+ final int index = getWidgetIndex(item);
if (index != activeTabIndex && !disabled && !readonly
&& !disabledTabKeys.contains(tabKeys.get(index))) {
addStyleDependentName("loading");
// HEIGHT
if (!isDynamicHeight()) {
int usedPixels = 0;
- for (Iterator iterator = stack.iterator(); iterator.hasNext();) {
- StackItem item = (StackItem) iterator.next();
+ for (Widget w : getChildren()) {
+ StackItem item = (StackItem) w;
if (item == openTab) {
usedPixels += item.getCaptionHeight();
} else {
if (isDynamicWidth()) {
int maxWidth = 40;
- for (StackItem si : stack) {
+ for (Widget w : getChildren()) {
+ StackItem si = (StackItem) w;
int captionWidth = si.getCaptionWidth();
if (captionWidth > maxWidth) {
maxWidth = captionWidth;
}
}
+ public Widget getComponent() {
+ if (getWidgetCount() < 2) {
+ return null;
+ }
+ return getWidget(1);
+ }
+
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
@Override
protected void clearPaintables() {
- stack.clear();
clear();
}
}
public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- for (StackItem item : stack) {
+ for (Widget w : getChildren()) {
+ StackItem item = (StackItem) w;
if (item.getPaintable() == oldComponent) {
item.replacePaintable((Paintable) newComponent);
return;
}
public void updateCaption(Paintable component, UIDL uidl) {
- for (Iterator iterator = stack.iterator(); iterator.hasNext();) {
- StackItem si = (StackItem) iterator.next();
+ for (Widget w : getChildren()) {
+ StackItem si = (StackItem) w;
if (si.getPaintable() == component) {
boolean visible = si.isVisible();
si.updateCaption(uidl);
@Override
protected int getTabCount() {
- return stack.size();
+ return getWidgetCount();
}
@Override
protected void removeTab(int index) {
- StackItem item = stack.get(index);
+ StackItem item = getStackItem(index);
remove(item);
}
+ @Override
+ protected Paintable getTab(int index) {
+ if (index < getWidgetCount()) {
+ return (Paintable) (getStackItem(index)).getPaintable();
+ }
+
+ return null;
+ }
+
+ private StackItem getStackItem(int index) {
+ return (StackItem) getWidget(index);
+ }
}
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.ComplexPanel;
-import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
import com.itmill.toolkit.terminal.gwt.client.ICaption;
*/
c.setWidth(c.getRequiredWidth() + "px");
captions.put("" + index, c);
+
+ UIDL tabContentUIDL = null;
+ Paintable tabContent = null;
+ if (tabUidl.getChildCount() > 0) {
+ tabContentUIDL = tabUidl.getChildUIDL(0);
+ tabContent = client.getPaintable(tabContentUIDL);
+ }
+
+ if (tabContent != null) {
+ /* This is a tab with content information */
+
+ int oldIndex = tp.getWidgetIndex((Widget) tabContent);
+ if (oldIndex != -1 && oldIndex != index) {
+ /*
+ * The tab has previously been rendered in another position so
+ * we must move the cached content to correct position
+ */
+ tp.insert((Widget) tabContent, index);
+ }
+ } else {
+ /* A tab whose content has not yet been loaded */
+
+ /*
+ * Make sure there is a corresponding empty tab in tp. The same
+ * operation as the moving above but for not-loaded tabs.
+ */
+ if (index < tp.getWidgetCount()) {
+ Widget oldWidget = tp.getWidget(index);
+ if (!(oldWidget instanceof PlaceHolder)) {
+ tp.insert(new PlaceHolder(), index);
+ }
+ }
+
+ }
+
if (selected) {
- renderContent(tabUidl.getChildUIDL(0));
+ renderContent(tabContentUIDL);
tb.selectTab(index);
} else {
- if (tabUidl.getChildCount() > 0) {
+ if (tabContentUIDL != null) {
// updating a drawn child on hidden tab
- Paintable paintable = client.getPaintable(tabUidl
- .getChildUIDL(0));
-
- if (tp.getWidgetIndex((Widget) paintable) < 0) {
- tp.insert((Widget) paintable, index);
+ if (tp.getWidgetIndex((Widget) tabContent) < 0) {
+ tp.insert((Widget) tabContent, index);
}
- paintable.updateFromUIDL(tabUidl.getChildUIDL(0), client);
+ tabContent.updateFromUIDL(tabContentUIDL, client);
} else if (tp.getWidgetCount() <= index) {
- tp.add(new Label(""));
+ tp.add(new PlaceHolder());
}
}
}
+ public class PlaceHolder extends ILabel {
+ public PlaceHolder() {
+ super("");
+ }
+ }
+
@Override
protected void selectTab(int index, final UIDL contentUidl) {
if (index != activeTabIndex) {
return tb.getWidgetCount();
}
+ @Override
+ protected Paintable getTab(int index) {
+ if (tp.getWidgetCount() > index) {
+ return (Paintable) tp.getWidget(index);
+ }
+ return null;
+ }
+
@Override
protected void removeTab(int index) {
tb.removeTab(index);
// Render content
final UIDL tabs = uidl.getChildUIDL(0);
+
+ // Paintables in the TabSheet before update
ArrayList oldPaintables = new ArrayList();
for (Iterator iterator = getPaintableIterator(); iterator.hasNext();) {
oldPaintables.add(iterator.next());
if (selected) {
activeTabIndex = index;
}
- if (tab.getChildCount() > 0) {
- Paintable p = client.getPaintable(tab.getChildUIDL(0));
- oldPaintables.remove(p);
- }
renderTab(tab, index, selected, hidden);
index++;
}
removeTab(index);
}
+ for (int i = 0; i < getTabCount(); i++) {
+ Paintable p = getTab(i);
+ oldPaintables.remove(p);
+ }
+
+ // Perform unregister for any paintables removed during update
for (Iterator iterator = oldPaintables.iterator(); iterator.hasNext();) {
Object oldPaintable = iterator.next();
if (oldPaintable instanceof Paintable) {
*/
protected abstract int getTabCount();
+ /**
+ * Implement in extending classes. This method should return the Paintable
+ * corresponding to the given index.
+ */
+ protected abstract Paintable getTab(int index);
+
/**
* Implement in extending classes. This method should remove the rendered
* tab with the specified index.
*/\r
public void insert(Widget w, int beforeIndex) {\r
Element el = createContainerElement();\r
- super.insert(w, el, beforeIndex, false);\r
DOM.insertChild(getElement(), el, beforeIndex);\r
+ super.insert(w, el, beforeIndex, false);\r
}\r
\r
@Override\r
public boolean remove(Widget w) {\r
- final int index = getWidgetIndex(w);\r
+ Element child = w.getElement();\r
+ Element parent = null;\r
+ if (child != null) {\r
+ parent = DOM.getParent(child);\r
+ }\r
final boolean removed = super.remove(w);\r
if (removed) {\r
if (visibleWidget == w) {\r
visibleWidget = null;\r
}\r
- Element child = DOM.getChild(getElement(), index);\r
- DOM.removeChild(getElement(), child);\r
+ if (parent != null) {\r
+ DOM.removeChild(getElement(), parent);\r
+ }\r
}\r
return removed;\r
}\r
--- /dev/null
+package com.itmill.toolkit.tests.components;\r
+\r
+import com.itmill.toolkit.Application;\r
+import com.itmill.toolkit.ui.Label;\r
+import com.itmill.toolkit.ui.Layout;\r
+import com.itmill.toolkit.ui.SplitPanel;\r
+import com.itmill.toolkit.ui.VerticalLayout;\r
+import com.itmill.toolkit.ui.Window;\r
+\r
+public abstract class TestBase extends Application {\r
+\r
+ @Override\r
+ public final void init() {\r
+ window = new Window(getClass().getName());\r
+ setMainWindow(window);\r
+ window.getLayout().setSizeFull();\r
+\r
+ Label label = new Label(getDescription());\r
+ label.setWidth("100%");\r
+ window.getLayout().addComponent(label);\r
+\r
+ layout = new VerticalLayout();\r
+ window.getLayout().addComponent(layout);\r
+ ((VerticalLayout) window.getLayout()).setExpandRatio(layout, 1);\r
+\r
+ setup();\r
+ }\r
+\r
+ private Window window;\r
+ private SplitPanel splitPanel;\r
+ private Layout layout;\r
+\r
+ public TestBase() {\r
+\r
+ }\r
+\r
+ protected Layout getLayout() {\r
+ return layout;\r
+ }\r
+\r
+ protected abstract String getDescription();\r
+\r
+ protected abstract void setup();\r
+\r
+}\r
--- /dev/null
+package com.itmill.toolkit.tests.components.accordion;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import com.itmill.toolkit.tests.components.TestBase;\r
+import com.itmill.toolkit.ui.AbstractComponentContainer;\r
+import com.itmill.toolkit.ui.Accordion;\r
+import com.itmill.toolkit.ui.Button;\r
+import com.itmill.toolkit.ui.Component;\r
+import com.itmill.toolkit.ui.Label;\r
+import com.itmill.toolkit.ui.Button.ClickEvent;\r
+\r
+public class RemoveTabs extends TestBase {\r
+\r
+ private Accordion accordion;\r
+\r
+ protected Component[] tab = new Component[5];\r
+\r
+ private Button closeCurrent;\r
+ private Button closeFirst;\r
+ private Button closeLast;\r
+ private Button reorderTabs;\r
+\r
+ @Override\r
+ protected String getDescription() {\r
+ return "Tests the removal of individual tabs from an Accordion. No matter what is done in this test the tab caption \"Tab X\" should always match the content \"Tab X\". Use \"remove first\" and \"remove active\" buttons to remove the first or the active tab. The \"reorder\" button reverses the order by adding and removing all components.";\r
+ }\r
+\r
+ @Override\r
+ protected void setup() {\r
+ accordion = new Accordion();\r
+ for (int i = 1; i <= tab.length; i++) {\r
+ tab[i - 1] = new Label("This is the contents of tab " + i);\r
+ tab[i - 1].setCaption("Tab " + i);\r
+\r
+ accordion.addComponent(tab[i - 1]);\r
+ }\r
+\r
+ getLayout().addComponent(accordion);\r
+\r
+ closeCurrent = new Button("Close current tab");\r
+ closeCurrent.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeCurrentTab();\r
+\r
+ }\r
+ });\r
+\r
+ closeFirst = new Button("close first tab");\r
+ closeFirst.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeFirstTab();\r
+\r
+ }\r
+ });\r
+\r
+ closeLast = new Button("close last tab");\r
+ closeLast.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeLastTab();\r
+\r
+ }\r
+ });\r
+\r
+ reorderTabs = new Button("reorder");\r
+ reorderTabs.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ reorder();\r
+\r
+ }\r
+ });\r
+\r
+ getLayout().addComponent(closeFirst);\r
+ getLayout().addComponent(closeLast);\r
+ getLayout().addComponent(closeCurrent);\r
+ getLayout().addComponent(reorderTabs);\r
+\r
+ }\r
+\r
+ private void closeCurrentTab() {\r
+ Component c = accordion.getSelectedTab();\r
+ if (c != null) {\r
+ accordion.removeComponent(c);\r
+ }\r
+ }\r
+\r
+ private void closeFirstTab() {\r
+ accordion.removeComponent((Component) accordion.getComponentIterator()\r
+ .next());\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private void closeLastTab() {\r
+ Iterator i = accordion.getComponentIterator();\r
+ Component last = null;\r
+ while (i.hasNext()) {\r
+ last = (Component) i.next();\r
+\r
+ }\r
+ accordion.removeComponent(last);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private void reorder() {\r
+ AbstractComponentContainer container = accordion;\r
+\r
+ if (container != null) {\r
+ List<Component> c = new ArrayList<Component>();\r
+ Iterator<Component> i = container.getComponentIterator();\r
+ while (i.hasNext()) {\r
+ Component comp = i.next();\r
+ c.add(comp);\r
+ }\r
+ container.removeAllComponents();\r
+\r
+ for (int j = c.size() - 1; j >= 0; j--) {\r
+ container.addComponent(c.get(j));\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+package com.itmill.toolkit.tests.components.tabsheet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import com.itmill.toolkit.tests.components.TestBase;\r
+import com.itmill.toolkit.ui.AbstractComponentContainer;\r
+import com.itmill.toolkit.ui.Button;\r
+import com.itmill.toolkit.ui.Component;\r
+import com.itmill.toolkit.ui.Label;\r
+import com.itmill.toolkit.ui.TabSheet;\r
+import com.itmill.toolkit.ui.Button.ClickEvent;\r
+\r
+public class RemoveTabs extends TestBase {\r
+\r
+ protected TabSheet tabsheet;\r
+\r
+ protected Component[] tab = new Component[5];\r
+\r
+ private Button closeCurrent;\r
+ private Button closeFirst;\r
+ private Button closeLast;\r
+ private Button reorderTabs;\r
+\r
+ @Override\r
+ protected String getDescription() {\r
+ return "Tests the removal of individual tabs from a Tabsheet. No matter what is done in this test the tab caption \"Tab X\" should always match the content \"Tab X\". Use \"remove first\" and \"remove active\" buttons to remove the first or the active tab. The \"reorder\" button reverses the order by adding and removing all components.";\r
+ }\r
+\r
+ @Override\r
+ protected void setup() {\r
+ tabsheet = new TabSheet();\r
+ for (int i = 1; i <= tab.length; i++) {\r
+ tab[i - 1] = new Label("This is the contents of tab " + i);\r
+ tab[i - 1].setCaption("Tab " + i);\r
+\r
+ tabsheet.addComponent(tab[i - 1]);\r
+ }\r
+\r
+ getLayout().addComponent(tabsheet);\r
+\r
+ closeCurrent = new Button("Close current tab");\r
+ closeCurrent.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeCurrentTab();\r
+\r
+ }\r
+ });\r
+\r
+ closeFirst = new Button("close first tab");\r
+ closeFirst.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeFirstTab();\r
+\r
+ }\r
+ });\r
+\r
+ closeLast = new Button("close last tab");\r
+ closeLast.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ closeLastTab();\r
+\r
+ }\r
+ });\r
+\r
+ reorderTabs = new Button("reorder");\r
+ reorderTabs.addListener(new Button.ClickListener() {\r
+ public void buttonClick(ClickEvent event) {\r
+ reorder();\r
+\r
+ }\r
+ });\r
+\r
+ getLayout().addComponent(closeFirst);\r
+ getLayout().addComponent(closeLast);\r
+ getLayout().addComponent(closeCurrent);\r
+ getLayout().addComponent(reorderTabs);\r
+\r
+ }\r
+\r
+ private void closeCurrentTab() {\r
+ Component c = tabsheet.getSelectedTab();\r
+ if (c != null) {\r
+ tabsheet.removeComponent(c);\r
+ }\r
+ }\r
+\r
+ private void closeFirstTab() {\r
+ tabsheet.removeComponent((Component) tabsheet.getComponentIterator()\r
+ .next());\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private void closeLastTab() {\r
+ Iterator i = tabsheet.getComponentIterator();\r
+ Component last = null;\r
+ while (i.hasNext()) {\r
+ last = (Component) i.next();\r
+\r
+ }\r
+ tabsheet.removeComponent(last);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private void reorder() {\r
+ AbstractComponentContainer container = tabsheet;\r
+\r
+ if (container != null) {\r
+ List<Component> c = new ArrayList<Component>();\r
+ Iterator<Component> i = container.getComponentIterator();\r
+ while (i.hasNext()) {\r
+ Component comp = i.next();\r
+ c.add(comp);\r
+ }\r
+ container.removeAllComponents();\r
+\r
+ for (int j = c.size() - 1; j >= 0; j--) {\r
+ container.addComponent(c.get(j));\r
+ }\r
+\r
+ }\r
+ }\r
+}\r