com.extjs.gxt.ui.client.widget.TabPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gxt Show documentation
Show all versions of gxt Show documentation
Rich Internet Application Framework for GWT
/*
* Sencha GXT 2.3.1a - Sencha for GWT
* Copyright(c) 2007-2013, Sencha, Inc.
* [email protected]
*
* http://www.sencha.com/products/gxt/license/
*/
package com.extjs.gxt.ui.client.widget;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.aria.FocusManager;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.Template;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.ContainerEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FxEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.event.TabPanelEvent;
import com.extjs.gxt.ui.client.fx.FxConfig;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.util.Params;
import com.extjs.gxt.ui.client.util.Size;
import com.extjs.gxt.ui.client.widget.TabItem.HeaderItem;
import com.extjs.gxt.ui.client.widget.layout.CardLayout;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Accessibility;
/**
* A basic tab container.
*
*
* TabPanel panel = new TabPanel();
* panel.setResizeTabs(true);
* panel.setEnableTabScroll(true);
* panel.setAnimScroll(true);
*
* TabItem item = new TabItem();
* item.setClosable(true);
* item.setText("Tab Item");
*
* item.setLayout(new FitLayout());
* item.add(new Label("Test Content"));
*
* panel.add(item);
*
*
*
* - Events:
*
* - BeforeSelect : TabPanelEvent(container, item)
* Fires after an item is selected. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.
*
* - container : this
* - item : the item about to be selected.
*
*
*
* - Select : TabPanelEvent(container, item)
* Fires after a item is selected.
*
* - container : this
* - item : the item that was selected
*
*
*
* - BeforeAdd : TabPanelEvent(container, item, index)
* Fires before a item is added or inserted. Listeners can cancel the
* action by calling {@link BaseEvent#setCancelled(boolean)}.
*
* - container : this
* - item : the item being added
* - index : the index at which the item will be added
*
*
*
* - BeforeRemove : TabPanelEvent(container, item)
* Fires before a item is removed. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.
*
* - container : this
* - item : the item being removed
*
*
*
* - Add : TabPanelEvent(container, item, index)
* Fires after a item has been added or inserted.
*
* - container : this
* - item : the item that was added
* - index : the index at which the item will be added
*
*
*
* - Remove : TabPanelEvent(container, item)
* Fires after a item has been removed.
*
* - container : this
* - item : the item being removed
*
*
*
*
*
* - Inherited Events:
* - BoxComponent Move
* - BoxComponent Resize
* - Component Enable
* - Component Disable
* - Component BeforeHide
* - Component Hide
* - Component BeforeShow
* - Component Show
* - Component Attach
* - Component Detach
* - Component BeforeRender
* - Component Render
* - Component BrowserEvent
* - Component BeforeStateRestore
* - Component StateRestore
* - Component BeforeStateSave
* - Component SaveState
*
*/
@SuppressWarnings("deprecation")
public class TabPanel extends Container {
/**
* TabPanel messages.
*/
public class TabPanelMessages {
private String closeOtherText = GXT.MESSAGES.tabPanelItem_closeOtherText();
private String closeText = GXT.MESSAGES.tabPanelItem_closeText();
/**
* Returns the close other text.
*
* @return the close other text
*/
public String getCloseOtherText() {
return closeOtherText;
}
/**
* Returns the close text.
*
* @return the close text
*/
public String getCloseText() {
return closeText;
}
/**
* Sets the close other text for the close context menu (defaults to 'Close
* all other tabs').
*
* @param closeOtherText the close other text
*/
public void setCloseOtherText(String closeOtherText) {
this.closeOtherText = closeOtherText;
}
/**
* Sets the close text for the close context menu (default to 'Close this
* tab').
*
* @param closeText the close text
*/
public void setCloseText(String closeText) {
this.closeText = closeText;
}
}
/**
* Tab position enumeration.
*/
public enum TabPosition {
BOTTOM, TOP;
}
private class AccessStack {
Stack stack = new Stack();
void add(TabItem item) {
if (stack.contains(item)) {
stack.remove(item);
}
stack.add(item);
if (stack.size() > 10) {
stack.remove(0);
}
}
void clear() {
stack.clear();
}
TabItem next() {
return stack.size() > 0 ? stack.pop() : null;
}
void remove(TabItem item) {
stack.remove(item);
}
}
/**
* Default tab item template.
*/
public static Template itemTemplate;
protected Menu closeContextMenu;
private TabItem activeItem;
private boolean animScroll = false;
private boolean autoSelect = true;
private El body, bar, stripWrap, strip;
private boolean bodyBorder = true;
private boolean border = true;
private CardLayout cardLayout;
private boolean closeMenu = false;
private El edge, scrollLeft, scrollRight;
private TabPanelMessages messages;
private int minTabWidth = 30;
private boolean plain;
private boolean resizeTabs = false;
private int scrollDuration = 150;
private int scrollIncrement = 100;
private boolean scrolling;
private AccessStack stack;
private int tabMargin = 2;
private TabPosition tabPosition = TabPosition.TOP;
private boolean tabScroll = false;
private int tabWidth = 120;
/**
* Creates a new tab panel.
*/
public TabPanel() {
baseStyle = "x-tab-panel";
cardLayout = new CardLayout();
setLayout(cardLayout);
enableLayout = true;
setMessages(new TabPanelMessages());
setDeferHeight(true);
}
/**
* Adds a tab item. Fires the BeforeAdd event before inserting, then
* fires the Add event after the widget has been inserted.
*
* @param item the item to be added
*/
public boolean add(TabItem item) {
return super.add(item);
}
/**
* Searches for an item based on its id and optionally the item's text.
*
* @param id the item id
* @param checkText true to match the items id and text
* @return the item
*/
public TabItem findItem(String id, boolean checkText) {
int count = getItemCount();
for (int i = 0; i < count; i++) {
TabItem item = getItem(i);
if (item.getItemId().equals(id) || item.getId().equals(id) || (checkText && item.getHtml().equals(id))) {
return item;
}
}
return null;
}
/**
* Returns true if scrolling is animated.
*
* @return the anim scroll state
*/
public boolean getAnimScroll() {
return animScroll;
}
/**
* Returns true if the body border is enabled.
*
* @return the body border state
*/
public boolean getBodyBorder() {
return bodyBorder;
}
/**
* Returns true if the border style is enabled.
*
* @return the border style
*/
public boolean getBorderStyle() {
return border;
}
@Override
public CardLayout getLayout() {
return cardLayout;
}
@Override
public El getLayoutTarget() {
return body;
}
/**
* Returns the tab panel messages.
*
* @return the messages
*/
public TabPanelMessages getMessages() {
return messages;
}
/**
* Returns the minimum tab width.
*
* @return the minimum tab width
*/
public int getMinTabWidth() {
return minTabWidth;
}
/**
* Returns true if tab resizing is enabled.
*
* @return the tab resizing state
*/
public boolean getResizeTabs() {
return resizeTabs;
}
/**
* Returns the scroll duration in milliseconds.
*
* @return the duration
*/
public int getScrollDuration() {
return scrollDuration;
}
/**
* Returns the current selection tab item.
*
* @return the selected item
*/
public TabItem getSelectedItem() {
return activeItem;
}
/**
* Returns the panel's tab margin.
*
* @return the margin
*/
public int getTabMargin() {
return tabMargin;
}
/**
* Returns the tab position.
*
* @return the tab position
*/
public TabPosition getTabPosition() {
return tabPosition;
}
/**
* Returns true if tab scrolling is enabled.
*
* @return the tab scroll state
*/
public boolean getTabScroll() {
return tabScroll;
}
/**
* Returns the default tab width.
*
* @return the width
*/
public int getTabWidth() {
return tabWidth;
}
/**
* Adds a tab item. Fires the BeforeAdd event before inserting, then
* fires the Add event after the widget has been inserted.
*
* @param item the item to be inserted
* @param index the insert position
*/
@Override
public boolean insert(TabItem item, int index) {
return super.insert(item, index);
}
/**
* Returns true if auto select is enabled.
*
* @return the auto select state
*/
public boolean isAutoSelect() {
return autoSelect;
}
/**
* Returns true if close context menu is enabled.
*
* @return the close menu state
*/
public boolean isCloseContextMenu() {
return closeMenu;
}
/**
* Returns true if children items are rendered when first accessed.
*
* @return true to defer rendering
*/
public boolean isDeferredRender() {
return cardLayout.isDeferredRender();
}
/**
* Returns true if the tab strip will be rendered without a background.
*
* @return the plain state
*/
public boolean isPlain() {
return plain;
}
public void onComponentEvent(ComponentEvent ce) {
super.onComponentEvent(ce);
if (ce.getEventTypeInt() == Event.ONCLICK) {
El target = ce.getTargetEl();
if (target.is(".x-tab-scroller-left")) {
ce.cancelBubble();
onScrollLeft();
}
if (target.is(".x-tab-scroller-right")) {
ce.cancelBubble();
onScrollRight();
}
}
if (ce.getEventTypeInt() == Event.ONBLUR && GXT.isFocusManagerEnabled()) {
onBlur(ce);
} else if (ce.getEventTypeInt() == Event.ONFOCUS && GXT.isFocusManagerEnabled()) {
onFocus(ce);
}
}
/**
* Removes the tab item. Fires the BeforeRemove event before removing,
* then fires the Remove event after the widget has been removed.
*
* @param item the item to be removed
*/
public boolean remove(TabItem item) {
boolean removed = super.remove(item);
if (removed) {
if (stack != null) {
stack.remove(item);
}
if (rendered) {
if (item.header.isRendered()) {
item.header.removeStyleName("x-tab-strip-active");
strip.dom.removeChild(item.header.getElement());
ComponentHelper.doDetach(item.header);
}
if (item == activeItem) {
activeItem = null;
TabItem next = this.stack.next();
if (next != null) {
setSelection(next);
} else if (getItemCount() > 0) {
setSelection(getItem(0));
} else {
getLayout().activeItem = null;
}
}
}
delegateUpdates();
}
return removed;
}
@Override
public boolean removeAll() {
int count = getItemCount();
TabItem current = getSelectedItem();
for (int i = count - 1; i >= 0; i--) {
TabItem item = getItem(i);
if (current != item) {
remove(item);
}
}
if (current != null) {
remove(current);
}
return getItemCount() == 0;
}
/**
* Scrolls to a particular tab if tab scrolling is enabled.
*
* @param item the item to scroll to
* @param animate true to animate the scroll
*/
public void scrollToTab(TabItem item, boolean animate) {
if (item == null) return;
int pos = getScollPos();
int area = getScrollArea();
El itemEl = item.header.el();
int left = itemEl.getOffsetsTo(stripWrap.dom).x + pos;
int right = left + itemEl.getWidth();
if (left < pos) {
scrollTo(left, animate);
} else if (right > (pos + area)) {
scrollTo(right - area, animate);
}
}
/**
* True to animate tab scrolling so that hidden tabs slide smoothly into view
* (defaults to true). Only applies when {@link #tabScroll} = true.
*
* @param animScroll the anim scroll state
*/
public void setAnimScroll(boolean animScroll) {
this.animScroll = animScroll;
}
@Override
public void setAutoHeight(boolean autoHeight) {
super.setAutoHeight(autoHeight);
for (TabItem item : getItems()) {
item.setAutoHeight(autoHeight);
}
}
/**
* True to have the first item selected when the panel is displayed for the
* first time if there is not selection (defaults to true).
*
* @param autoSelect the auto select state
*/
public void setAutoSelect(boolean autoSelect) {
this.autoSelect = autoSelect;
}
/**
* True to display an interior border on the body element of the panel, false
* to hide it (defaults to true, pre-render).
*
* @param bodyBorder the body border style
*/
public void setBodyBorder(boolean bodyBorder) {
this.bodyBorder = bodyBorder;
}
/**
* True to display a border around the tabs (defaults to true).
*
* @param border true for borders
*/
public void setBorderStyle(boolean border) {
this.border = border;
}
/**
* True to show the close context menu (defaults to false).
*
* @param closeMenu true to show it
*/
public void setCloseContextMenu(boolean closeMenu) {
this.closeMenu = closeMenu;
}
/**
* True to render each child tab item when it accessed, false to render all
* (defaults to true). Setting to false would be useful when using forms as
* validation would need to be applied to all children even if they had not
* been selected.
*
* @param deferredRender true to defer rendering
*/
public void setDeferredRender(boolean deferredRender) {
cardLayout.setDeferredRender(deferredRender);
}
/**
* Sets the tab panel messages.
*
* @param messages the messages
*/
public void setMessages(TabPanelMessages messages) {
this.messages = messages;
}
/**
* The minimum width in pixels for each tab when {@link #resizeTabs} = true
* (defaults to 30).
*
* @param minTabWidth the minimum tab width
*/
public void setMinTabWidth(int minTabWidth) {
this.minTabWidth = minTabWidth;
}
/**
* True to render the tab strip without a background container image (defaults
* to false, pre-render).
*
* @param plain
*/
public void setPlain(boolean plain) {
assertPreRender();
this.plain = plain;
}
/**
* True to automatically resize each tab so that the tabs will completely fill
* the tab strip (defaults to false). Setting this to true may cause specific
* widths that might be set per tab to be overridden in order to fit them all
* into view (although {@link #minTabWidth} will always be honored).
*
* @param resizeTabs true to enable tab resizing
*/
public void setResizeTabs(boolean resizeTabs) {
this.resizeTabs = resizeTabs;
}
/**
* Sets the number of milliseconds that each scroll animation should last
* (defaults to 150).
*
* @param scrollDuration the scroll duration
*/
public void setScrollDuration(int scrollDuration) {
this.scrollDuration = scrollDuration;
}
/**
* Sets the number of pixels to scroll each time a tab scroll button is
* pressed (defaults to 100, or if {@link #setResizeTabs(boolean)} = true, the
* calculated tab width). Only applies when {@link #setTabScroll(boolean)} =
* true.
*
* @param scrollIncrement the scroll increment
*/
public void setScrollIncrement(int scrollIncrement) {
this.scrollIncrement = scrollIncrement;
}
/**
* Sets the selected tab item. Fires the BeforeSelect event before
* selecting, then fires the Select event after the widget has been
* selected.
*
* @param item the item to be selected
*/
public void setSelection(TabItem item) {
TabPanelEvent tpe = new TabPanelEvent(this, item);
if (item == null || item.getParent() != this || !fireEvent(Events.BeforeSelect, tpe) || !item.fireEvent(Events.BeforeSelect, tpe)) {
return;
}
if (!rendered) {
activeItem = item;
return;
}
if (activeItem != item) {
if (activeItem != null) {
activeItem.header.removeStyleName("x-tab-strip-active");
if (GXT.isAriaEnabled()) {
Accessibility.setState(activeItem.header.getElement(), "aria-selected", "false");
}
}
item.header.addStyleName("x-tab-strip-active");
if (GXT.isAriaEnabled()) {
Accessibility.setState(item.header.getElement(), "aria-selected", "true");
}
activeItem = item;
stack.add(activeItem);
cardLayout.setActiveItem(activeItem);
if (scrolling) {
scrollToTab(item, getAnimScroll());
}
focusTab(activeItem, false);
fireEvent(Events.Select, tpe);
item.fireEvent(Events.Select, tpe);
}
if (GXT.isFocusManagerEnabled() && activeItem == item) {
focusTab(activeItem, false);
}
}
/**
* The number of pixels of space to calculate into the sizing and scrolling of
* tabs (defaults to 2).
*
* @param tabMargin the tab margin
*/
public void setTabMargin(int tabMargin) {
this.tabMargin = tabMargin;
}
/**
* Sets the position where the tab strip should be rendered (defaults to TOP,
* pre-render). The only other supported value is BOTTOM. Note that tab
* scrolling is only supported for position TOP.
*
* @param tabPosition the tab position
*/
public void setTabPosition(TabPosition tabPosition) {
this.tabPosition = tabPosition;
}
/**
* True to enable scrolling to tabs that may be invisible due to overflowing
* the overall TabPanel width. Only available with tabs on top. (defaults to
* false).
*
* @param tabScroll true to enable tab scrolling
*/
public void setTabScroll(boolean tabScroll) {
this.tabScroll = tabScroll;
}
/**
* Sets the initial width in pixels of each new tab (defaults to 120).
*
* @param tabWidth
*/
public void setTabWidth(int tabWidth) {
this.tabWidth = tabWidth;
}
protected void close(TabItem item) {
TabPanelEvent e = new TabPanelEvent(this, item);
if (item.isClosable() && item.fireEvent(Events.BeforeClose, e) && remove(item)) {
item.fireEvent(Events.Close, new TabPanelEvent(this, item));
}
}
@Override
protected ComponentEvent createComponentEvent(Event event) {
return new TabPanelEvent(this);
}
@Override
@SuppressWarnings("rawtypes")
protected ContainerEvent createContainerEvent(TabItem item) {
return new TabPanelEvent(this, item);
}
@Override
protected void doAttachChildren() {
super.doAttachChildren();
for (TabItem item : getItems()) {
ComponentHelper.doAttach(item.header);
}
}
@Override
protected void doDetachChildren() {
super.doDetachChildren();
for (TabItem item : getItems()) {
ComponentHelper.doDetach(item.header);
}
}
@Override
protected void onAfterLayout() {
super.onAfterLayout();
delegateUpdates();
}
@Override
protected void onAttach() {
super.onAttach();
bar.disableTextSelection(true);
if (activeItem != null) {
TabItem item = activeItem;
activeItem = null;
setSelection(item);
} else if (activeItem == null && autoSelect && getItemCount() > 0) {
setSelection(getItem(0));
}
if (GXT.isFocusManagerEnabled()) {
FocusFrame.get().unframe();
}
if (resizeTabs) {
autoSizeTabs();
}
}
protected void onBlur(ComponentEvent ce) {
FocusFrame.get().unframe();
}
@Override
protected void onDetach() {
bar.disableTextSelection(false);
super.onDetach();
}
protected void onFocus(ComponentEvent ce) {
FocusFrame.get().frame(this);
if (GXT.isFocusManagerEnabled() && !FocusManager.get().isManaged()) {
return;
}
if (getItemCount() > 0 && getSelectedItem() == null) {
setSelection(getItem(0));
} else if (getSelectedItem() != null) {
focusTab(getSelectedItem(), true);
DeferredCommand.addCommand(new Command() {
public void execute() {
FocusFrame.get().frame(TabPanel.this);
}
});
}
}
protected void onInsert(TabItem item, int index) {
super.onInsert(item, index);
item.tabPanel = this;
item.setAutoHeight(isAutoHeight());
if (rendered) {
renderItem(item, index);
if (isAttached()) {
ComponentHelper.doAttach(item.header);
}
if (activeItem == null && autoSelect) {
setSelection(item);
}
delegateUpdates();
if (getItemCount() == 1) {
syncSize();
}
}
}
protected void onItemContextMenu(TabItem item, int x, int y) {
if (closeMenu) {
if (closeContextMenu == null) {
closeContextMenu = new Menu();
closeContextMenu.addListener(Events.Hide, new Listener() {
public void handleEvent(MenuEvent be) {
be.getContainer().setData("tab", null);
}
});
closeContextMenu.add(new MenuItem(messages.getCloseText(), new SelectionListener() {
@Override
public void componentSelected(MenuEvent ce) {
TabItem item = (TabItem) ce.getContainer().getData("tab");
close(item);
}
}));
closeContextMenu.add(new MenuItem(messages.getCloseOtherText(), new SelectionListener() {
@Override
public void componentSelected(MenuEvent ce) {
TabItem item = (TabItem) ce.getContainer().getData("tab");
List items = new ArrayList();
items.addAll(getItems());
for (TabItem currentItem : items) {
if (currentItem != item && currentItem.isClosable()) {
close(currentItem);
}
}
}
}));
}
closeContextMenu.getItem(0).setEnabled(item.isClosable());
closeContextMenu.setData("tab", item);
boolean hasClosable = false;
for (TabItem item2 : getItems()) {
if (item2.isClosable() && item2 != item) {
hasClosable = true;
break;
}
}
closeContextMenu.getItem(1).setEnabled(hasClosable);
closeContextMenu.showAt(x, y);
}
}
protected void onItemTextChange(TabItem tabItem, String oldText, String newText) {
delegateUpdates();
}
protected void onKeyPress(ComponentEvent ce) {
int code = ce.getKeyCode();
switch (code) {
case KeyCodes.KEY_RIGHT:
case KeyCodes.KEY_PAGEDOWN:
onRight(ce);
break;
case KeyCodes.KEY_LEFT:
case KeyCodes.KEY_PAGEUP:
onLeft(ce);
break;
case KeyCodes.KEY_HOME:
if (ce.getTarget() == activeItem.getHeader().getElement() && getItemCount() > 0 && activeItem != getItem(0)) {
setSelection(getItem(0));
}
break;
case KeyCodes.KEY_END:
if (ce.getTarget() == activeItem.getHeader().getElement()) {
setSelection(getItem(getItemCount() - 1));
}
break;
case KeyCodes.KEY_ENTER:
if (GXT.isFocusManagerEnabled()) {
Component c = ComponentManager.get().find(ce.getTarget());
if (c != null && c instanceof HeaderItem) {
TabItem ti = (TabItem) ((HeaderItem) c).getParent();
if (activeItem != ti) {
setSelection(ti);
ce.preventDefault();
}
}
}
break;
}
}
protected void onLeft(ComponentEvent ce) {
if (activeItem != null && ce.getTarget() == activeItem.getHeader().getElement()) {
int idx = indexOf(activeItem);
if (idx > 0) {
setSelection(getItem(idx - 1));
focusTab(activeItem, true);
}
}
}
@Override
protected void onRemove(TabItem item) {
super.onRemove(item);
item.tabPanel = null;
}
@Override
protected void onRender(Element target, int index) {
setElement(DOM.createDiv(), target, index);
stack = new AccessStack();
if (tabPosition == TabPosition.TOP) {
bar = el().createChild("");
body = el().createChild("