package org.gwtoolbox.widget.client.panel.tab;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.*;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.event.logical.shared.*;
import com.google.gwt.event.shared.HandlerRegistration;
import org.gwtoolbox.commons.collections.client.EmptyIterator;
import org.gwtoolbox.commons.ui.client.event.EventUtils;
import org.gwtoolbox.commons.ui.client.event.Handler;
import org.gwtoolbox.commons.ui.client.event.HoverHandler;
import static org.gwtoolbox.commons.ui.client.event.EventUtils.addOnClickHandler;
import static org.gwtoolbox.commons.ui.client.event.EventUtils.addContextMenuListener;
import org.gwtoolbox.commons.ui.client.event.ContextMenuHandler;
import org.gwtoolbox.widget.client.menu.MenuBuilder;
import org.gwtoolbox.widget.client.panel.LayoutPanel;
import org.gwtoolbox.widget.client.panel.LayoutUtils;
import static org.gwtoolbox.widget.client.panel.LayoutUtils.*;
import org.gwtoolbox.widget.client.menu.MenuPopup;
import org.gwtoolbox.widget.client.panel.layout.tab.TabSpec;
import org.gwtoolbox.widget.client.popup.Popup;
import java.util.*;
/**
* @author Uri Boness
* @deprecated use {@link org.gwtoolbox.widget.client.panel.layout.tab.TabLayout instead}
*/
@Deprecated
public class SimpleTabBar extends LayoutPanel implements HasSelectionHandlers<SimpleTabBar.Tab>, HasCloseHandlers<SimpleTabBar.Tab> {
private final static Iterator<Widget> ITERATOR = new EmptyIterator<Widget>();
private final static int SCROLL_BUTTON_WIDTH = 18;
private final static int TAB_HORIZONTAL_MARGIN = 2;
private final static int SCROLL_JUMP = 100;
private Impl impl = GWT.create(Impl.class);
private Element container;
private Element leftScrollButton;
private Element tabsViewportElement;
private Element tabsElement;
private Element rightScrollButton;
private List<Tab> tabs = new ArrayList<Tab>();
private Map<String, Tab> tabById = new HashMap<String, Tab>();
private Tab selectedTab;
private ScrollStrategy scrollStrategy = new StaticScrollStrategy(SCROLL_JUMP);
public SimpleTabBar() {
impl.init(this);
Element main = preventBoxStyles(DOM.createDiv());
setElement(main);
container = DOM.createDiv();
addRelativePositoning(container);
DOM.setStyleAttribute(container, "height", impl.getContainerHeight());
DOM.setStyleAttribute(container, "borderBottomWidth", "1px");
DOM.setStyleAttribute(container, "borderBottomStyle", "solid");
DOM.setStyleAttribute(container, "overflow", "hidden");
DOM.setStyleAttribute(container, "margin", "0");
DOM.setStyleAttribute(container, "padding", "0 0 2px 0");
DOM.setStyleAttribute(container, "position", "relative");
DOM.setStyleAttribute(container, "left", "0");
DOM.setStyleAttribute(container, "top", "0");
container.setClassName("SimpleTabBar");
main.appendChild(container);
leftScrollButton = DOM.createDiv();
setClassname(leftScrollButton, "tabs-left-scroll-button");
DOM.appendChild(container, leftScrollButton);
setVisible(leftScrollButton, false);
DOM.setStyleAttribute(leftScrollButton, "position", "absolute");
DOM.setStyleAttribute(leftScrollButton, "left", "0");
DOM.setStyleAttribute(leftScrollButton, "top", "0");
DOM.setStyleAttribute(leftScrollButton, "width", "18px");
DOM.setStyleAttribute(leftScrollButton, "height", "23px");
DOM.setStyleAttribute(leftScrollButton, "borderBottomWidth", "1px");
DOM.setStyleAttribute(leftScrollButton, "borderBottomStyle", "solid");
DOM.setStyleAttribute(leftScrollButton, "cursor", "pointer");
addOnClickHandler(leftScrollButton, new Handler<Event>() {
public void handle(Event event) {
scrollLeft();
}
});
rightScrollButton = DOM.createDiv();
setClassname(rightScrollButton, "tabs-right-scroll-button");
DOM.appendChild(container, rightScrollButton);
setVisible(rightScrollButton, false);
DOM.setStyleAttribute(rightScrollButton, "position", "absolute");
DOM.setStyleAttribute(rightScrollButton, "top", "0");
DOM.setStyleAttribute(rightScrollButton, "width", "18px");
DOM.setStyleAttribute(rightScrollButton, "height", "23px");
DOM.setStyleAttribute(rightScrollButton, "borderBottomWidth", "1px");
DOM.setStyleAttribute(rightScrollButton, "borderBottomStyle", "solid");
DOM.setStyleAttribute(rightScrollButton, "cursor", "pointer");
addOnClickHandler(rightScrollButton, new Handler<Event>() {
public void handle(Event event) {
scrollRight();
}
});
tabsViewportElement = DOM.createDiv();
tabsViewportElement.setClassName("tabs-viewport");
DOM.setStyleAttribute(tabsViewportElement, "position", "absolute");
DOM.setStyleAttribute(tabsViewportElement, "left", "0px");
DOM.setStyleAttribute(tabsViewportElement, "height", impl.getTabsViewportHeight());
DOM.setStyleAttribute(tabsViewportElement, "overflow", "hidden");
DOM.setStyleAttribute(tabsViewportElement, "padding", "2px 0 0 0");
DOM.appendChild(container, tabsViewportElement);
tabsElement = DOM.createElement("ul");
tabsElement.setClassName("tabs");
DOM.appendChild(tabsViewportElement, tabsElement);
DOM.setStyleAttribute(tabsElement, "display", "block");
DOM.setStyleAttribute(tabsElement, "position", "relative");
DOM.setStyleAttribute(tabsElement, "width", "4000px");
DOM.setStyleAttribute(tabsElement, "height", "20px");
DOM.setStyleAttribute(tabsElement, "borderBottomWidth", "1px");
DOM.setStyleAttribute(tabsElement, "borderBottomStyle", "solid");
DOM.setStyleAttribute(tabsElement, "listStyleImage", "none");
DOM.setStyleAttribute(tabsElement, "listStylePosition", "outside");
DOM.setStyleAttribute(tabsElement, "listStyleType", "none");
DOM.setStyleAttribute(tabsElement, "margin", "0");
DOM.setStyleAttribute(tabsElement, "padding", "1px 0 0 0");
}
public void add(Widget widget) {
throw new UnsupportedOperationException("SimpleTabBar does not contain widgets");
}
public boolean remove(Widget child) {
throw new UnsupportedOperationException("SimpleTabBar does not contain widgets");
}
public void add(String text) {
add(text, false);
}
public void add(String id, String text) {
add(id, text, false);
}
public void add(String text, boolean closable) {
add(text, text, closable);
}
public void add(String id, String text, boolean closable) {
TabSpec spec = new TabSpec(id).setText(text).setClosable(closable);
addTab(spec);
}
public int getTabIndex(String id) {
Tab tab = tabById.get(id);
return tabs.indexOf(tab);
}
public String getTabId(int index) {
Tab tab = tabs.get(index);
return tab.id;
}
public void addTab(TabSpec tabSpec) {
insertTab(tabSpec, -1);
}
public void insertTab(TabSpec tabSpec, int index) {
assert tabSpec.getText() != null;
// creating the containing parent
final Element li = DOM.createElement("li");
setClassname(li, "tab");
Element closeButtonElement = null;
if (tabSpec.isClosable()) {
closeButtonElement = DOM.createDiv();
closeButtonElement.setClassName("tab-close-button");
closeButtonElement.setInnerHTML(" ");
DOM.appendChild(li, closeButtonElement);
}
Element leftDiv = DOM.createDiv();
leftDiv.setClassName("tab-left");
DOM.appendChild(li, leftDiv);
Element rightDiv = DOM.createDiv();
rightDiv.setClassName("tab-right");
DOM.appendChild(leftDiv, rightDiv);
Element centerSpan = DOM.createSpan();
centerSpan.setClassName("tab-center");
Integer width = tabSpec.getWidth();
if (width == null) {
width = impl.getTextWidth(tabSpec.getText(), "tab-text-measurement");
}
DOM.setStyleAttribute(centerSpan, "width", width + "px");
DOM.appendChild(rightDiv, centerSpan);
Element textSpan = DOM.createSpan();
textSpan.setClassName("tab-text");
textSpan.setInnerText(tabSpec.getText());
DOM.appendChild(centerSpan, textSpan);
final Tab tab = new Tab(tabSpec, li, textSpan, closeButtonElement);
if (index > -1) {
tabs.add(index, tab);
tab.setIndex(index);
} else {
tabs.add(tab);
tab.setIndex(tabs.size() - 1);
}
tabById.put(tabSpec.getId(), tab);
// Physical attach new.
DOM.appendChild(tabsElement, li);
if (isAttached()) {
impl.layout();
}
}
public HandlerRegistration addSelectionHandler(SelectionHandler<Tab> tabSelectionHandler) {
return addHandler(tabSelectionHandler, SelectionEvent.getType());
}
public HandlerRegistration addCloseHandler(CloseHandler<Tab> tabCloseHandler) {
return addHandler(tabCloseHandler, CloseEvent.getType());
}
public Tab getSelectedTab() {
return selectedTab;
}
public int getSelectedTabIndex() {
return tabs.indexOf(selectedTab);
}
public void selectTab(String id) {
Tab tab = tabById.get(id);
tab.select();
}
public int getTabCount() {
return tabs.size();
}
public void setTabEnabled(String id, boolean enabled) {
tabById.get(id).setEnabled(enabled);
}
public boolean isTabEnabled(String id) {
return tabById.get(id).isEnabled();
}
public Tab remove(String id) {
Tab tab = tabById.remove(id);
if (tab == null) {
return null;
}
tabs.indexOf(tab);
tabs.remove(tab);
if (selectedTab != null && selectedTab == tab) {
selectedTab.unselect();
}
DOM.removeChild(tabsElement, tab.tabElement);
impl.layout();
return tab;
}
public void clear() {
for (Tab tab : tabs.toArray(new Tab[tabs.size()])) {
remove(tab.id);
}
}
public Iterator<Widget> iterator() {
return ITERATOR;
}
public void layout() {
impl.layout();
}
@Override
protected void onLoad() {
impl.onAttach();
}
@Override
protected void onUnload() {
impl.onDetach();
}
public void setScrollJump(int jump) {
scrollStrategy = new StaticScrollStrategy(jump);
}
public void setScrollJumpToSmallestTab() {
scrollStrategy = new SmallestTabScrollStrategy();
if (isAttached()) {
scrollStrategy.update(this);
}
}
public void setScrollJumpToLargestTab() {
scrollStrategy = new LargestTabScrollStrategy();
if (isAttached()) {
scrollStrategy.update(this);
}
}
//================================================ Helper Methods ==================================================
private void scrollLeft() {
int left = tabsElement.getOffsetLeft();
int targetLeft = 0;
if (left <= - scrollStrategy.getScrollJump()) {
targetLeft = left + scrollStrategy.getScrollJump();
}
new SlideLeftAnimation(left, targetLeft, tabsElement).start();
}
private void scrollRight() {
Element lastTabElement = tabs.get(tabs.size() - 1).tabElement;
int lastTabRight = lastTabElement.getAbsoluteLeft() + LayoutUtils.getOffsetWidth(lastTabElement);
int tabsElementRight = tabsViewportElement.getAbsoluteLeft() + LayoutUtils.getOffsetWidth(tabsViewportElement);
int delta = lastTabRight - tabsElementRight;
int left = tabsElement.getOffsetLeft();
int targetLeft;
if (delta < scrollStrategy.getScrollJump()) {
targetLeft = tabsElement.getOffsetLeft() - delta - TAB_HORIZONTAL_MARGIN;
} else {
targetLeft = tabsElement.getOffsetLeft() - scrollStrategy.getScrollJump();
}
new SlideLeftAnimation(left, targetLeft, tabsElement).start();
}
//================================================= Inner Classes ==================================================
private class SlideLeftAnimation extends Animation {
private int from;
private int to;
private Element element;
private final int delta;
private SlideLeftAnimation(int from, int to, Element element) {
this.from = from;
this.to = to;
this.element = element;
delta = to - from;
}
protected void onUpdate(double progress) {
if (progress == 1.0) {
setLeft(element, to + "px");
return;
}
int interval = (int) (progress * delta);
setLeft(element, from + interval + "px");
}
public void start() {
run(300);
}
}
private static class Impl {
protected SimpleTabBar bar;
public void init(SimpleTabBar panel) {
this.bar = panel;
}
public String getContainerHeight() {
return "25px";
}
public String getTabsViewportHeight() {
return "22px";
}
public void onAttach() {
layout();
DeferredCommand.addCommand(new Command() {
public void execute() {
layout();
}
});
}
public void onDetach() {
}
public void layout() {
bar.setVisible(true);
int containerHeight = LayoutUtils.getOffsetHeight(bar.container);
if (containerHeight == 0) {
return;
}
if (bar.tabs.isEmpty()) {
setLeft(bar.tabsViewportElement, "0px");
LayoutUtils.setWidth(bar.tabsViewportElement, "100%");
setVisible(bar.leftScrollButton, false);
setVisible(bar.rightScrollButton, false);
return;
}
setLeft(bar.tabsElement, "0px");
Element lastTabElement = bar.tabs.get(bar.tabs.size() - 1).tabElement;
int lastTabLeft = lastTabElement.getAbsoluteLeft() - bar.tabsElement.getAbsoluteLeft();
int tabsWidth = lastTabLeft + LayoutUtils.getOffsetWidth(lastTabElement);
int containerWidth = LayoutUtils.getOffsetWidth(bar.container);
if (tabsWidth > containerWidth) {
setLeft(bar.tabsViewportElement, SCROLL_BUTTON_WIDTH + "px");
LayoutUtils.setWidth(bar.tabsViewportElement, (containerWidth - 2 * SCROLL_BUTTON_WIDTH) + "px");
setVisible(bar.leftScrollButton, true);
setLeft(bar.rightScrollButton, (containerWidth - SCROLL_BUTTON_WIDTH) + "px");
setVisible(bar.rightScrollButton, true);
} else {
setLeft(bar.tabsViewportElement, "0px");
LayoutUtils.setWidth(bar.tabsViewportElement, "100%");
setVisible(bar.leftScrollButton, false);
setVisible(bar.rightScrollButton, false);
}
bar.scrollStrategy.update(bar);
bar.notifyLayout();
}
public int getTextWidth(String text, String className) {
return LayoutUtils.getTextWidth(text, className);
}
}
private static class ImplIE6 extends Impl {
@Override
public String getContainerHeight() {
return "27px";
}
@Override
public String getTabsViewportHeight() {
return "23px";
}
public void onAttach() {
addResizeListener(bar.container);
layout();
}
public void onDetach() {
DOM.setElementProperty(bar.container, "onresize", null);
}
private native void addResizeListener(Element container) /*-{
var self = this;
container.onresize = function() {
self.@org.gwtoolbox.widget.client.panel.tab.SimpleTabBar$ImplIE6::layout()();
};
}-*/;
@Override
public int getTextWidth(String text, String className) {
int width = super.getTextWidth(text, className);
width += 26; //adding the padding;
return width;
}
}
public class Tab {
private String id;
private String text;
private int index;
private Element tabElement;
private Element textElement;
private Element closeButtonElement;
private boolean enabled = true;
private Tab(TabSpec tabSpec, Element tabElement, Element textElement, final Element closeButtonElement) {
this.id = tabSpec.getId();
this.text = tabSpec.getText();
this.tabElement = tabElement;
this.textElement = textElement;
this.closeButtonElement = closeButtonElement;
addOnClickHandler(tabElement, new Handler<Event>() {
public void handle(Event event) {
handleOnClick();
}
});
EventUtils.addMouseHoverHandler(tabElement, new HoverHandler() {
public void onMouseOver(Event event) {
handleMouseOver();
}
public void onMouseOut(Event event) {
handleMouseOut();
}
});
if (closeButtonElement != null) {
addOnClickHandler(closeButtonElement, new Handler<Event>() {
public void handle(Event event) {
handleOnClose();
}
});
EventUtils.addMouseHoverHandler(closeButtonElement, new HoverHandler() {
public void onMouseOver(Event event) {
setStyleName(closeButtonElement, "tab-close-button-hover", true);
}
public void onMouseOut(Event event) {
setStyleName(closeButtonElement, "tab-close-button-hover", false);
}
});
}
final MenuBuilder menuBuilder = tabSpec.getMenuBuilder();
if (menuBuilder != null) {
addContextMenuListener(tabElement, new ContextMenuHandler() {
public void onContextMenu(final com.google.gwt.dom.client.Element element, final int x, final int y) {
final MenuPopup menu = new MenuPopup(true);
menuBuilder.build(menu.getMenu());
menu.setPopupPositionAndShow(new Popup.PositionCallback() {
public void setPosition(int offsetWidth, int offsetHeight) {
menu.setPopupPosition(element.getAbsoluteLeft() + x, element.getAbsoluteTop() + y);
}
});
}
});
}
// addContextMenuListener(tabElement, new ContextMenuHandler() {
// public void onContextMenu(final com.google.gwt.dom.client.Element element, final int x, final int y) {
// final MenuPopup menu = new MenuPopup(true);
// if (!isSelected()) {
// menu.addItem("Select", new Command() {
// public void execute() {
// select();
// }
// }).setEnabled(enabled);
// }
// if (closeButtonElement != null) {
// menu.addItem("Close", new Command() {
// public void execute() {
// handleOnClose();
// }
// }).setEnabled(enabled);
// }
// menu.addItem("Close Others", new Command() {
// public void execute() {
// handleCloseAll();
// }
// }).setEnabled(enabled);
// menu.setPopupPositionAndShow(new Popup.PositionCallback() {
// public void setPosition(int offsetWidth, int offsetHeight) {
// menu.setPopupPosition(element.getAbsoluteLeft() + x, element.getAbsoluteTop() + y);
// }
// });
// }
// });
}
public boolean isEnabled() {
return enabled;
}
public boolean isSelected() {
return this == selectedTab;
}
public String getId() {
return id;
}
public String getText() {
return text;
}
public int getIndex() {
return index;
}
private void setIndex(int index) {
this.index = index;
}
private void setEnabled(boolean enabled) {
this.enabled = enabled;
if (isSelected() && !enabled) {
unselect();
}
markEnabled(enabled);
}
private void handleMouseOver() {
if (!isSelected() && enabled) {
setStyleName(textElement, "tab-text-hover", true);
}
}
private void handleMouseOut() {
if (!isSelected() && enabled) {
setStyleName(textElement, "tab-text-hover", false);
}
}
private void handleOnClick() {
if (enabled) {
select();
}
}
private void handleOnClose() {
if (enabled) {
Tab tab = remove(id);
CloseEvent.fire(SimpleTabBar.this, tab);
}
}
private void handleCloseAll() {
for (Tab tab : tabs) {
if (tab != this && tab.isClosable()) {
tab.handleOnClose();
}
}
}
public boolean isClosable() {
return closeButtonElement != null;
}
public void select() {
if (selectedTab != null) {
selectedTab.markSelected(false);
}
selectedTab = this;
selectedTab.markSelected(true);
SelectionEvent.fire(SimpleTabBar.this, this);
}
public void unselect() {
markSelected(false);
selectedTab = null;
SelectionEvent.fire(SimpleTabBar.this, null);
}
private void markSelected(boolean selected) {
if (selected) {
setClassname(tabElement, "tab-selected");
} else {
setClassname(tabElement, "tab");
}
}
private void markEnabled(boolean enabled) {
if (!enabled) {
setClassname(tabElement, "tab-disabled");
} else {
if (isSelected()) {
setClassname(tabElement, "tab-selected");
} else {
setClassname(tabElement, "tab");
}
}
}
}
private interface ScrollStrategy {
int getScrollJump();
void update(SimpleTabBar tabBar);
}
private class StaticScrollStrategy implements ScrollStrategy {
private final int jump;
private StaticScrollStrategy(int jump) {
this.jump = jump;
}
public int getScrollJump() {
return jump;
}
public void update(SimpleTabBar tabBar) {
}
}
private class SmallestTabScrollStrategy implements ScrollStrategy {
private int jump;
private SmallestTabScrollStrategy() {
update(SimpleTabBar.this);
}
public int getScrollJump() {
return jump;
}
public void update(SimpleTabBar tabBar) {
int width = 4000;
for (Tab tab : tabBar.tabs) {
int offsetWidth = tab.tabElement.getOffsetWidth();
if (width > offsetWidth) {
width = offsetWidth;
}
}
this.jump = width;
}
}
private class LargestTabScrollStrategy implements ScrollStrategy {
private int jump;
private LargestTabScrollStrategy() {
update(SimpleTabBar.this);
}
public int getScrollJump() {
return jump;
}
public void update(SimpleTabBar tabBar) {
int width = 0;
for (Tab tab : tabBar.tabs) {
int offsetWidth = tab.tabElement.getOffsetWidth();
if (width < offsetWidth) {
width = offsetWidth;
}
}
this.jump = width;
}
}
}