bufferings.ktr.wjr.client.ui.widget.WjrTabPanel.java Source code

Java tutorial

Introduction

Here is the source code for bufferings.ktr.wjr.client.ui.widget.WjrTabPanel.java

Source

/*
 * Copyright 2010 bufferings[at]gmail.com
 *
 * 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 bufferings.ktr.wjr.client.ui.widget;

import static bufferings.ktr.wjr.client.ui.widget.JQueryUI.*;
import static bufferings.ktr.wjr.shared.util.Preconditions.*;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
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.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.IndexedPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.ProvidesResize;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.WidgetCollection;

/**
 * The tab panel with JQueryUI theme.
 * 
 * @author bufferings[at]gmail.com
 */
public class WjrTabPanel extends ResizeComposite implements ProvidesResize, IndexedPanel,
        HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer> {

    private static WjrTabPanelUiBinder uiBinder = GWT.create(WjrTabPanelUiBinder.class);

    interface WjrTabPanelUiBinder extends UiBinder<Widget, WjrTabPanel> {
    }

    /**
     * CssResource used in the WjrTabPanel.
     * 
     * @author bufferings[at]gmail.com
     */
    protected interface MyStyle extends CssResource {

        /**
         * The tab selected style.
         * 
         * @return The tab selected style.
         */
        String tabSelected();

        /**
         * The tab unselected style.
         * 
         * @return The tab unselected style.
         */
        String tabUnselected();
    }

    protected static final int PADDING = 3;

    protected static final double BAR_HEIGHT = 35;

    /**
     * The css style.
     */
    @UiField
    protected MyStyle style;

    /**
     * The layout panel to control tabs and contents.
     */
    @UiField
    protected LayoutPanel panel;

    /**
     * The tab bar panel.
     */
    @UiField
    protected HorizontalPanel tabBar;

    /**
     * The tab widgets
     */
    protected ArrayList<Tab> tabs;

    /**
     * The content widgets.
     */
    protected WidgetCollection children;

    /**
     * The tab selected index.
     */
    protected int selectedIndex = -1;

    /**
     * Constructs the WjrTabPanel.
     */
    public WjrTabPanel() {
        initWidget(uiBinder.createAndBindUi(this));

        tabs = new ArrayList<Tab>();
        children = new WidgetCollection(panel);
    }

    /**
     * {@inheritDoc}
     */
    public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<Integer> handler) {
        return addHandler(handler, BeforeSelectionEvent.getType());
    }

    /**
     * {@inheritDoc}
     */
    public HandlerRegistration addSelectionHandler(SelectionHandler<Integer> handler) {
        return addHandler(handler, SelectionEvent.getType());
    }

    /**
     * Adds the tab item.
     * 
     * @param child
     *          The child tab contents widget.
     * @param text
     *          The tab text.
     * @param tabStopIndex
     *          The tab index.
     */
    public void add(Widget child, String text, int tabStopIndex) {
        insert(child, text, getWidgetCount(), tabStopIndex);
    }

    /**
     * Inserts the tab item.
     * 
     * @param child
     *          The child tab contents widget.
     * @param text
     *          The tab text.
     * @param beforeIndex
     *          The index to insert into.
     * @param tabStopIndex
     *          The tab index.
     */
    public void insert(final Widget child, String text, int beforeIndex, int tabStopIndex) {
        checkArgument((beforeIndex >= 0) && (beforeIndex <= getWidgetCount()), "beforeIndex out of bounds");

        int idx = getWidgetIndex(child);
        if (idx != -1) {
            remove(child);
            if (idx < beforeIndex) {
                beforeIndex--;
            }
        }

        final Tab tab = new Tab(text, tabStopIndex);
        children.insert(child, beforeIndex);
        tabs.add(beforeIndex, tab);

        tabBar.insert(tab, beforeIndex);
        tab.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                selectTab(child);
            }
        });

        panel.insert(child, beforeIndex);
        layoutChild(child);

        if (selectedIndex == -1) {
            selectTab(0);
        }
    }

    /**
     * Clears all the tabs and the contents.
     */
    public void clear() {
        Iterator<Widget> it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }

    /**
     * Gets the selected index.
     * 
     * @return The selected index.
     */
    public int getSelectedIndex() {
        return selectedIndex;
    }

    /**
     * {@inheritDoc}
     */
    public Widget getWidget(int index) {
        checkIndex(index);
        return children.get(index);
    }

    /**
     * {@inheritDoc}
     */
    public int getWidgetCount() {
        return children.size();
    }

    /**
     * {@inheritDoc}
     */
    public int getWidgetIndex(Widget child) {
        return children.indexOf(child);
    }

    /**
     * Gets the iterator of children widgets.
     * 
     * @return The iterator of children widgets.
     */
    public Iterator<Widget> iterator() {
        return children.iterator();
    }

    /**
     * {@inheritDoc}
     */
    public boolean remove(int index) {
        if ((index < 0) || (index >= getWidgetCount())) {
            return false;
        }

        Widget child = children.get(index);
        tabBar.remove(index);
        panel.remove(child);

        children.remove(index);
        tabs.remove(index);

        if (index == selectedIndex) {
            selectedIndex = -1;
            if (getWidgetCount() > 0) {
                selectTab(0);
            }
        } else if (index < selectedIndex) {
            --selectedIndex;
        }
        return true;
    }

    /**
     * Removes the widget.
     * 
     * If the widget is not a child of this composite, then return false.
     * 
     * @param w
     *          The widget to remove.
     * @return False if the widget is not present, true if present.
     */
    public boolean remove(Widget w) {
        int index = children.indexOf(w);
        if (index == -1) {
            return false;
        }
        return remove(index);
    }

    /**
     * Selects the tab and show its contents.
     * 
     * @param index
     *          The index of the tab to show.
     */
    public void selectTab(int index) {
        checkIndex(index);
        if (index == selectedIndex) {
            return;
        }

        BeforeSelectionEvent<Integer> event = BeforeSelectionEvent.fire(this, index);
        if ((event != null) && event.isCanceled()) {
            return;
        }

        if (selectedIndex != -1) {
            Widget child = children.get(selectedIndex);
            Element container = panel.getWidgetContainerElement(child);
            container.getStyle().setDisplay(Display.NONE);
            child.setVisible(false);
            tabs.get(selectedIndex).setSelected(false);
        }

        Widget child = children.get(index);
        Element container = panel.getWidgetContainerElement(child);
        container.getStyle().clearDisplay();
        child.setVisible(true);
        tabs.get(index).setSelected(true);
        selectedIndex = index;

        SelectionEvent.fire(this, index);
    }

    /**
     * Selects the tab from the contents.
     * 
     * @param child
     *          The contents to show.
     */
    public void selectTab(Widget child) {
        selectTab(getWidgetIndex(child));
    }

    /**
     * Checks the index is valid.
     * 
     * @param index
     *          The index to check.
     * @throws IllegalArgumentException
     *           if the index is out of bounds.
     */
    protected void checkIndex(int index) {
        checkArgument((index >= 0) && (index < getWidgetCount()), "Index out of bounds");
    }

    /**
     * Layouts the content widget.
     * 
     * @param child
     *          The content widget.
     */
    protected void layoutChild(Widget child) {
        panel.setWidgetLeftRight(child, PADDING, Unit.PX, PADDING, Unit.PX);
        panel.setWidgetTopBottom(child, BAR_HEIGHT + PADDING, Unit.PX, 0, Unit.PX);
        panel.getWidgetContainerElement(child).getStyle().setDisplay(Display.NONE);
        child.setVisible(false);
    }

    /**
     * The label for tab with JQueryUI style.
     * 
     * @author bufferings[at]gmail.com
     */
    protected class Tab extends Composite {

        /**
         * For controling key events.
         */
        protected boolean lastWasKeyDown;

        /**
         * Constructs the Tab
         * 
         * @param text
         *          the text to show.
         * @param tabStopIndex
         *          The tab index.
         */
        public Tab(String text, int tabStopIndex) {
            super();

            Label label = new Label(text);
            label.setStyleName("");

            FocusPanel focusPanel = new FocusPanel();
            focusPanel.add(label);
            focusPanel.setTabIndex(tabStopIndex);
            initWidget(focusPanel);

            setStyleName(join(UI_WIDGET, UI_STATE_DEFAULT, UI_CORNER_TOP));
            setSelected(false);
            sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.KEYEVENTS);
        }

        /**
         * {@inheritDoc}
         */
        public void onBrowserEvent(Event event) {
            int eventType = DOM.eventGetType(event);

            switch (eventType) {
            case Event.ONMOUSEOVER:
                addStyleName(UI_STATE_HOVER);
                break;
            case Event.ONMOUSEOUT:
                removeStyleName(UI_STATE_HOVER);
                break;
            }

            switch (eventType) {
            case Event.ONKEYDOWN:
            case Event.ONKEYPRESS:
            case Event.ONKEYUP:
                if (DOM.eventGetAltKey(event) || DOM.eventGetMetaKey(event)) {
                    super.onBrowserEvent(event);
                    return;
                }
            }
            switch (eventType) {
            case Event.ONKEYDOWN: {
                keyboardNavigation(event);
                lastWasKeyDown = true;
                break;
            }
            case Event.ONKEYPRESS: {
                if (!lastWasKeyDown) {
                    keyboardNavigation(event);
                }
                lastWasKeyDown = false;
                break;
            }
            case Event.ONKEYUP: {
                lastWasKeyDown = false;
                break;
            }
            }

            switch (eventType) {
            case Event.ONKEYDOWN:
            case Event.ONKEYUP: {
                if (isKeyAssigned(DOM.eventGetKeyCode(event))) {
                    DOM.eventCancelBubble(event, true);
                    DOM.eventPreventDefault(event);
                    return;
                }
            }
            }

            super.onBrowserEvent(event);
        }

        /**
         * {@inheritDoc}
         */
        public HandlerRegistration addClickHandler(ClickHandler handler) {
            return addDomHandler(handler, ClickEvent.getType());
        }

        /**
         * Sets the selected style.
         * 
         * @param selected
         *          The selected value.
         */
        public void setSelected(boolean selected) {
            if (selected) {
                addStyleName(UI_STATE_ACTIVE);
                addStyleName(style.tabSelected());
                removeStyleName(style.tabUnselected());
            } else {
                removeStyleName(UI_STATE_ACTIVE);
                removeStyleName(style.tabSelected());
                addStyleName(style.tabUnselected());
            }
        }

        /**
         * Check if the key code is assigned or not.
         * 
         * @param code
         *          The key code.
         * @return True if the key is assigned, false if not.
         */
        protected boolean isKeyAssigned(int code) {
            switch (code) {
            case KeyCodes.KEY_ENTER:
                return true;
            default:
                return false;
            }
        }

        private void keyboardNavigation(Event event) {
            int code = DOM.eventGetKeyCode(event);
            switch (code) {
            case KeyCodes.KEY_ENTER: {
                NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false,
                        false);
                ClickEvent.fireNativeEvent(nativeEvent, this);
                break;
            }
            }
        }
    }

}