Java tutorial
/* * Copyright 2008 Google Inc. * * 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 cc.alcina.framework.gwt.client.widget; import java.util.ArrayList; import java.util.List; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.HasClickHandlers; import com.google.gwt.event.dom.client.HasKeyDownHandlers; 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.logical.shared.BeforeSelectionEvent; import com.google.gwt.event.logical.shared.BeforeSelectionHandler; import com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers; import com.google.gwt.event.logical.shared.HasSelectionHandlers; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Accessibility; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.SourcesClickEvents; import com.google.gwt.user.client.ui.TabListener; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.Widget; /** * Mostly lifted from GWT TabBar class */ @SuppressWarnings("deprecation") public class FlowTabBar extends Composite implements HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer>, ClickHandler, KeyDownHandler { private static final String STYLENAME_DEFAULT = "gwt-TabBarItem"; private FlowPanel panel2 = new FlowPanel(); private Widget selectedTab; private List<ClickDelegatePanel> tabs = new ArrayList<ClickDelegatePanel>(); /** * Creates an empty tab bar. */ public FlowTabBar() { initWidget(panel2); sinkEvents(Event.ONCLICK); setStyleName("gwt-TabBar"); // Add a11y role "tablist" Accessibility.setRole(panel2.getElement(), Accessibility.ROLE_TABLIST); // panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM); HTML first = new HTML(" ", true), rest = new HTML(" ", true); first.setStyleName("gwt-TabBarFirst"); rest.setStyleName("gwt-TabBarRest"); first.setHeight("100%"); rest.setHeight("100%"); first.setVisible(false); rest.setVisible(false); panel2.add(first); panel2.add(rest); first.setHeight("100%"); // panel.setCellHeight(first, "100%"); // panel.setCellWidth(rest, "100%"); } public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<Integer> handler) { return addHandler(handler, BeforeSelectionEvent.getType()); } public HandlerRegistration addSelectionHandler(SelectionHandler<Integer> handler) { return addHandler(handler, SelectionEvent.getType()); } /** * Adds a new tab with the specified text. * * @param text * the new tab's text */ public void addTab(String text) { insertTab(text, getTabCount()); } /** * Adds a new tab with the specified text. * * @param text * the new tab's text * @param asHTML * <code>true</code> to treat the specified text as html */ public void addTab(String text, boolean asHTML) { insertTab(text, asHTML, getTabCount()); } /** * Adds a new tab with the specified widget. * * @param widget * the new tab's widget. */ public void addTab(Widget widget) { insertTab(widget, getTabCount()); } public void allTabsAdded() { if (getTabCount() != 0) { tabs.get(0).addStyleName("TabBarFirst"); tabs.get(getTabCount() - 1).addStyleName("TabBarLast"); } } /** * Gets the tab that is currently selected. * * @return the selected tab */ public int getSelectedTab() { if (selectedTab == null) { return -1; } return tabs.indexOf(selectedTab); } /** * Gets the number of tabs present. * * @return the tab count */ public int getTabCount() { return tabs.size(); } /** * Gets the specified tab's HTML. * * @param index * the index of the tab whose HTML is to be retrieved * @return the tab's HTML */ public String getTabHTML(int index) { if (index >= getTabCount()) { return null; } ClickDelegatePanel delPanel = tabs.get(index); SimplePanel focusablePanel = delPanel.getFocusablePanel(); Widget widget = focusablePanel.getWidget(); if (widget instanceof HTML) { return ((HTML) widget).getHTML(); } else if (widget instanceof Label) { return ((Label) widget).getText(); } else { // This will be a focusable panel holding a user-supplied widget. return focusablePanel.getElement().getParentElement().getInnerHTML(); } } public void insertCaption(String text, String className, boolean separator) { if (separator) { Label label = new Label("\u00A0"); label.setStyleName("flowTabBar-separator"); panel2.add(label); } Label label = new Label(text); if (className != null) { label.addStyleName(className); } panel2.add(label); } /** * Inserts a new tab at the specified index. * * @param text * the new tab's text * @param asHTML * <code>true</code> to treat the specified text as HTML * @param beforeIndex * the index before which this tab will be inserted */ public void insertTab(String text, boolean asHTML, int beforeIndex) { checkInsertBeforeTabIndex(beforeIndex); Label item; if (asHTML) { item = new HTML(text); } else { item = new Label(text); } item.setWordWrap(false); insertTabWidget(item, beforeIndex); } /** * Inserts a new tab at the specified index. * * @param text * the new tab's text * @param beforeIndex * the index before which this tab will be inserted */ public void insertTab(String text, int beforeIndex) { insertTab(text, false, beforeIndex); } /** * Inserts a new tab at the specified index. * * @param widget * widget to be used in the new tab. * @param beforeIndex * the index before which this tab will be inserted. */ public void insertTab(Widget widget, int beforeIndex) { insertTabWidget(widget, beforeIndex); } public void onClick(ClickEvent event) { selectTabByTabWidget((Widget) event.getSource()); } public void onKeyDown(KeyDownEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { selectTabByTabWidget((Widget) event.getSource()); } } /** * Removes the tab at the specified index. * * @param index * the index of the tab to be removed */ public void removeTab(int index) { checkTabIndex(index); ClickDelegatePanel toRemove = tabs.get(index); if (toRemove == selectedTab) { selectedTab = null; } panel2.remove(toRemove); tabs.remove(toRemove); } /** * Programmatically selects the specified tab. Use index -1 to specify that * no tab should be selected. * * @param index * the index of the tab to be selected. * @return <code>true</code> if successful, <code>false</code> if the change * is denied by the {@link TabListener}. */ public boolean selectTab(int index) { BeforeSelectionEvent<?> event = BeforeSelectionEvent.fire(this, index); if (event != null && event.isCanceled()) { return false; } // Check for -1. setSelectionStyle(selectedTab, false); if (index == -1) { selectedTab = null; return true; } selectedTab = tabs.get(index); setSelectionStyle(selectedTab, true); SelectionEvent.fire(this, index); return true; } /** * Sets a tab's contents via HTML. * * Use care when setting an object's HTML; it is an easy way to expose * script-based security problems. Consider using * {@link #setTabText(int, String)} whenever possible. * * @param index * the index of the tab whose HTML is to be set * @param html * the tab new HTML */ public void setTabHTML(int index, String html) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; ClickDelegatePanel delPanel = tabs.get(index); SimplePanel focusablePanel = delPanel.getFocusablePanel(); focusablePanel.setWidget(new HTML(html)); } /** * Sets a tab's text contents. * * @param index * the index of the tab whose text is to be set * @param text * the object's new text */ public void setTabText(int index, String text) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; ClickDelegatePanel delPanel = tabs.get(index); SimplePanel focusablePanel = delPanel.getFocusablePanel(); // It is not safe to check if the current widget is an instanceof Label // and // reuse it here because HTML is an instanceof Label. Leaving an HTML // would // throw off the results of getTabHTML(int). focusablePanel.setWidget(new Label(text)); } private void checkInsertBeforeTabIndex(int beforeIndex) { if ((beforeIndex < 0) || (beforeIndex > getTabCount())) { throw new IndexOutOfBoundsException(); } } private void checkTabIndex(int index) { if ((index < -1) || (index >= getTabCount())) { throw new IndexOutOfBoundsException(); } } /** * Selects the tab corresponding to the widget for the tab. To be clear the * widget for the tab is not the widget INSIDE of the tab; it is the widget * used to represent the tab itself. * * @param tabWidget * The widget for the tab to be selected * @return true if the tab corresponding to the widget for the tab could * located and selected, false otherwise */ private boolean selectTabByTabWidget(Widget tabWidget) { int index = tabs.indexOf(tabWidget); if (index >= 0) { return selectTab(index); } return false; } private void setSelectionStyle(Widget item, boolean selected) { if (item != null) { if (selected) { item.addStyleName("gwt-TabBarItem-selected"); setStyleName(DOM.getParent(item.getElement()), "gwt-TabBarItem-wrapper-selected", true); } else { item.removeStyleName("gwt-TabBarItem-selected"); setStyleName(DOM.getParent(item.getElement()), "gwt-TabBarItem-wrapper-selected", false); } } } /** * Create a {@link SimplePanel} that will wrap the contents in a tab. * Subclasses can use this method to wrap tabs in decorator panels. * * @return a {@link SimplePanel} to wrap the tab contents, or null to leave * tabs unwrapped */ protected SimplePanel createTabTextWrapper() { return null; } /** * Inserts a new tab at the specified index. * * @param widget * widget to be used in the new tab. * @param beforeIndex * the index before which this tab will be inserted. */ protected void insertTabWidget(Widget widget, int beforeIndex) { checkInsertBeforeTabIndex(beforeIndex); ClickDelegatePanel delWidget = new ClickDelegatePanel(widget); delWidget.addClickHandler(this); delWidget.addKeyDownHandler(this); delWidget.setStyleName(STYLENAME_DEFAULT); // Add a11y role "tab" SimplePanel focusablePanel = delWidget.getFocusablePanel(); Accessibility.setRole(focusablePanel.getElement(), Accessibility.ROLE_TAB); if (beforeIndex == tabs.size()) { panel2.add(delWidget); } else { panel2.insert(delWidget, panel2.getWidgetIndex(tabs.get(beforeIndex))); } tabs.add(delWidget); setStyleName(DOM.getParent(delWidget.getElement()), STYLENAME_DEFAULT + "-wrapper", true); } /** * <b>Affected Elements:</b> * <ul> * <li>-tab# = The element containing the contents of the tab.</li> * <li>-tab-wrapper# = The cell containing the tab at the index.</li> * </ul> * * @see UIObject#onEnsureDebugId(String) */ @Override protected void onEnsureDebugId(String baseID) { super.onEnsureDebugId(baseID); int numTabs = getTabCount(); for (int i = 0; i < numTabs; i++) { ClickDelegatePanel delPanel = tabs.get(i); SimplePanel focusablePanel = delPanel.getFocusablePanel(); // ensureDebugId(focusablePanel.getContainerElement(), baseID, "tab" // + i); ensureDebugId(DOM.getParent(delPanel.getElement()), baseID, "tab-wrapper" + i); } } /** * <code>ClickDelegatePanel</code> decorates any widget with the minimal * amount of machinery to receive clicks for delegation to the parent. * {@link SourcesClickEvents} is not implemented due to the fact that only a * single observer is needed. */ private class ClickDelegatePanel extends Composite implements HasClickHandlers, HasKeyDownHandlers { private SimplePanel focusablePanel; ClickDelegatePanel(Widget child) { focusablePanel = new FocusPanel(); // allows wrapping Element span = createHiddenSpan(); DOM.insertChild(focusablePanel.getElement(), span, 2); Element spacer = new InlineLabel(" ").getElement(); DOM.insertChild(focusablePanel.getElement(), spacer, 3); focusablePanel.setWidget(child); SimplePanel wrapperWidget = createTabTextWrapper(); if (wrapperWidget == null) { initWidget(focusablePanel); } else { wrapperWidget.setWidget(focusablePanel); initWidget(wrapperWidget); } sinkEvents(Event.ONCLICK | Event.ONKEYDOWN); } public HandlerRegistration addClickHandler(ClickHandler handler) { return addDomHandler(handler, ClickEvent.getType()); } public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { return addDomHandler(handler, KeyDownEvent.getType()); } public SimplePanel getFocusablePanel() { return focusablePanel; } protected Element createHiddenSpan() { return Document.get().createSpanElement(); } protected native Element createHiddenSpan0() /*-{ var span = $doc.createElement('span'); span.style.width = span.style.height = 0; span.style.opacity = 0; span.style.zIndex = -1; span.style.height = '1px'; span.style.width = '1px'; span.style.overflow = 'hidden'; span.style.position = 'absolute'; return span; }-*/; } }