package org.vaadin.touchkit.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedList;
import com.vaadin.server.Resource;
import com.vaadin.ui.AbstractComponentContainer;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.TabSheet.Tab;
import com.vaadin.util.ReflectTools;
/**
* The TabBarView is a tabsheet implementation that mimics the native tabsheet
* (TabBarView) in iOS. By default a bar with equally sized tab buttons is shown
* below the tab content.
*/
public class TabBarView extends AbstractComponentContainer {
private Toolbar toolbar = new Toolbar();
private Component currentComponent;
private LinkedList<Component> tabs = new LinkedList<Component>();
/**
* Constructs a {@link TabBarView} that is 100% wide and high.
*/
public TabBarView() {
super();
setSizeFull();
super.addComponent(toolbar);
}
/**
* Adds a new sheet to the {@link TabBarView}, and adds a button
* representing it to the tab bar. The button gets it's caption and icon
* from the content.
*
* @param tabContent
* the sheet content
* @return tab 'meta-data' that can be used to configure the tab further
* @see Tab
*
*/
public Tab addTab(Component tabContent) {
return addTab(tabContent, tabContent.getCaption(), tabContent.getIcon());
}
/**
* Adds a new sheet to the {@link TabBarView}, and adds a button
* representing it to the tab bar. The button gets the given caption and no
* icon.
*
* @param tabContent
* the sheet content
* @param caption
* the caption to be used in the tab bar button
* @return tab 'meta-data' that can be used to configure the tab further
* @see Tab
*
*/
public Tab addTab(Component tabContent, String caption) {
return addTab(tabContent, caption, null);
}
/**
* Adds a new sheet to the {@link TabBarView}, and adds a button
* representing it to the tab bar. The button gets the given caption and
* icon.
*
* @param tabContent
* the sheet content
* @param caption
* the caption to be used in the tab bar button
* @param icon
* the icon to be used in the tab bar button
* @return tab 'meta-data' that can be used to configure the tab further
* @see Tab
*
*/
public Tab addTab(Component tabContent, String caption, Resource icon) {
TabButton tabButton = new TabButton(tabContent);
toolbar.addComponent(tabButton);
if (currentComponent == null) {
setSelectedTab(tabContent);
}
tabs.add(tabContent);
tabButton.setCaption(caption);
tabButton.setIcon(icon);
return tabButton;
}
/**
* @deprecated The behavior differs from regular {@link ComponentContainer}
* s, use the specialized API instead:
* {@link #addTab(Component)} and {@link #removeTab(Component)}
*
* @throws UnsupportedOperationException
*/
@Deprecated
@Override
public void addComponent(Component c) {
throw new UnsupportedOperationException(
"Use the specialized API instead.");
}
/**
* Removes the given tab content and the tab button associated with it.
*
* @param c
* the tab content to remove
*/
public void removeTab(Component c) {
TabButton tabButton = getTabButton(c);
tabs.remove(c);
if (tabButton != null) {
toolbar.removeComponent(tabButton);
if (c == currentComponent) {
super.removeComponent(c);
Component next = tabs.iterator().next();
if (next != null) {
setSelectedTab(next);
}
}
}
}
/**
* @deprecated The behavior differs from regular {@link ComponentContainer}
* s, use the specialized API instead:
* {@link #addTab(Component)} and {@link #removeTab(Component)}
*
* @throws UnsupportedOperationException
*/
@Deprecated
@Override
public void removeComponent(Component c) {
throw new UnsupportedOperationException(
"Use the specialized API instead.");
}
/**
* Removes the given tab (content and tab-bar button) from the TabBarView
*
* @param tab
* the tab to remove
*/
public void removeTab(Tab tab) {
removeTab(tab.getComponent());
}
/**
* Sets the active tab, i.e. sets the content as currently visible and marks
* its button in the tab-bar as active.
*
* @param tab
* the content to show
*/
public void setSelectedTab(Component tab) {
if (currentComponent != null) {
TabButton tabButton = getTabButton(currentComponent);
if (tabButton != null) {
tabButton.setSelected(false);
}
super.removeComponent(currentComponent);
}
super.addComponent(tab);
currentComponent = tab;
getTabButton(currentComponent).setSelected(true);
markAsDirty();
fireSelectedTabChange();
}
/**
* Sets the active {@link Tab}, i.e. sets the tab content as currently
* visible and marks its button in the tab-bar as active.
*
* @param tab
* the {@link Tab} to show
*/
public void setSelectedTab(Tab tab) {
setSelectedTab(tab.getComponent());
}
/**
* @return the currently selected Tab
*/
public Tab getSelelectedTab() {
return getTabButton(currentComponent);
}
/**
* @deprecated The behavior differs from regular {@link ComponentContainer}
* s, use the specialized API instead:
* {@link #addTab(Component)} and {@link #removeTab(Component)}
*
* @throws UnsupportedOperationException
*/
@Override
@Deprecated
public void replaceComponent(Component oldComponent, Component newComponent) {
throw new UnsupportedOperationException(
"Use the specialized API instead.");
}
/**
* Otherwise as {@link ComponentContainer#getComponentIterator()}, but note
* that a tab is not in the component hierarchy before it is activated, even
* tough it has been added with {@link #addTab(Component)}.
*
* @see ComponentContainer#getComponentIterator()
*/
@Override
public Iterator<Component> getComponentIterator() {
LinkedList<Component> list = new LinkedList<Component>();
list.add(toolbar);
if (currentComponent != null) {
list.add(currentComponent);
}
return list.iterator();
}
private TabButton getTabButton(Component tab) {
Iterator<Component> componentIterator = toolbar.getComponentIterator();
while (componentIterator.hasNext()) {
TabButton next = (TabButton) componentIterator.next();
if (next.getComponent() == tab) {
return next;
}
}
return null;
}
private class TabButton extends Button implements Tab {
private Component relatedComponent;
public TabButton(Component tab1) {
relatedComponent = tab1;
setCaption(tab1.getCaption());
addClickListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
setSelectedTab(getComponent());
}
});
}
public void setSelected(boolean b) {
if (b) {
addStyleName("selected");
} else {
removeStyleName("selected");
}
}
@Override
public boolean isClosable() {
return false;
}
@Override
public void setClosable(boolean closable) {
throw new UnsupportedOperationException();
}
@Override
public Component getComponent() {
return relatedComponent;
}
@Override
public void setDefaultFocusComponent(Focusable component) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Focusable getDefaultFocusComponent() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
private static final Method SELECTED_TAB_CHANGE_METHOD;
static {
SELECTED_TAB_CHANGE_METHOD = ReflectTools.findMethod(
SelectedTabChangeListener.class, "selectedTabChange",
new Class[] { SelectedTabChangeEvent.class });
}
/**
* Selected tab change event. This event is sent when the selected (shown)
* tab in the tab sheet is changed.
*
* @author Vaadin Ltd.
*/
public static class SelectedTabChangeEvent extends Component.Event {
/**
* Constructs a new instance of SelectedTabChangeEvent
*
* @param source
* the Source of the event.
*/
public SelectedTabChangeEvent(TabBarView source) {
super(source);
}
/**
* Gets the TabSheet where the event occurred.
*
* @return the Source of the event.
*/
public TabBarView getTabSheet() {
return (TabBarView) getSource();
}
}
/**
* Selected tab change event listener. The listener is called whenever
* another tab is selected, including when adding the first tab to a
* tabsheet.
*
* @author Vaadin Ltd.
*
*/
public interface SelectedTabChangeListener extends Serializable {
/**
* Selected (shown) tab in tab sheet has has been changed.
*
* @param event
* the selected tab change event.
*/
public void selectedTabChange(SelectedTabChangeEvent event);
}
/**
* Adds a tab selection listener
*
* @param listener
* the Listener to be added.
*/
public void addListener(SelectedTabChangeListener listener) {
addListener(SelectedTabChangeEvent.class, listener,
SELECTED_TAB_CHANGE_METHOD);
}
/**
* Removes a tab selection listener
*
* @param listener
* the Listener to be removed.
*/
public void removeListener(SelectedTabChangeListener listener) {
removeListener(SelectedTabChangeEvent.class, listener,
SELECTED_TAB_CHANGE_METHOD);
}
/**
* Sends an event that the currently selected tab has changed.
*/
protected void fireSelectedTabChange() {
fireEvent(new SelectedTabChangeEvent(this));
}
@Override
public int getComponentCount() {
return 1 + (currentComponent != null ? 1 : 0);
}
@Override
public Iterator<Component> iterator() {
return getComponentIterator();
}
}