Wednesday, April 3, 2013

Re: TabLayoutPanel with scroll buttons

I updated the code of Eze for GWT 2.5.
Apparently, the lastScroll trick is no longer needed, and I override the root insert / delete methods to reduce the number of overrides. There was also a little bug, using scrollLeftButton.getWidth() in place of scrollLeftButton.getHeight(), visible when the buttons are not square...
I renamed also some methods to my taste. :-)
There are still some things to improve, as I don't like the buttons to be over the first tab, but I will look into it later.

Here is my variant:

import com.google.gwt.core.client.Scheduler;
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.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * A {@link TabLayoutPanel} that shows scroll buttons if necessary.
 * https://groups.google.com/forum/?fromgroups=#!topic/google-web-toolkit/wN8lLU23wPA
 */
public class ScrollableTabLayoutPanel extends TabLayoutPanel
{
    private static final int IMAGE_PADDING_PIXELS = 4;
    private static final int SCROLL_INTERVAL = 60;

    private LayoutPanel panel;
    private FlowPanel tabBar;
    private HandlerRegistration windowResizeHandler;

    private Image scrollLeftButton;
    private Image scrollRightButton;
    private ImageResource leftArrowImage;
    private ImageResource rightArrowImage;

    public ScrollableTabLayoutPanel(double barHeight, Unit barUnit,
            ImageResource leftArrowImage, ImageResource rightArrowImage)
    {
        super(barHeight, barUnit);

        this.leftArrowImage = leftArrowImage;
        this.rightArrowImage = rightArrowImage;

        // The main widget wrapped by this composite, which is a LayoutPanel with the tab bar & the tab content
        panel = (LayoutPanel) getWidget();

        // Find the tab bar, which is the first flow panel in the LayoutPanel
        for (int i = 0; i < panel.getWidgetCount(); i++)
        {
            Widget widget = panel.getWidget(i);
            if (widget instanceof FlowPanel)
            {
                tabBar = (FlowPanel) widget;
                break;
            }
        }

        initScrollButtons();
    }


    @Override
    public void insert(Widget child, Widget tab, int beforeIndex)
    {
        super.insert(child, tab, beforeIndex);
        showScrollButtonsIfNecessary();
    }

    @Override
    public boolean remove(int index)
    {
        boolean b = super.remove(index);
        showScrollButtonsIfNecessary();
        return b;
    }


    @Override
    protected void onLoad()
    {
        super.onLoad();

        if (windowResizeHandler == null)
        {
            windowResizeHandler = Window.addResizeHandler(new ResizeHandler()
            {
                @Override
                public void onResize(ResizeEvent event)
                {
                    showScrollButtonsIfNecessary();
                }
            });
        }
    }

    @Override
    protected void onUnload()
    {
        super.onUnload();

        if (windowResizeHandler != null)
        {
            windowResizeHandler.removeHandler();
            windowResizeHandler = null;
        }
    }


    private ClickHandler createScrollClickHandler(final int diff)
    {
        return new ClickHandler()
        {
            @Override
            public void onClick(ClickEvent event)
            {
                Widget lastTab = getLastTab();
                if (lastTab == null)
                    return;

                int newLeft = parsePosition(tabBar.getElement().getStyle().getLeft()) + diff;
                int rightOfLastTab = getRightPosition(lastTab);

                // Prevent scrolling the last tab too far away form the right border,
                // or the first tab further than the left border position
                if (newLeft <= 0 && getTabBarWidth() - newLeft < (rightOfLastTab - diff / 2))
                {
                    scrollTo(newLeft);
                }
            }
        };
    }

    /**
     * Create and attach the scroll button images with a click handler
     */
    private void initScrollButtons()
    {
        scrollLeftButton = new Image(leftArrowImage);
        int leftImageWidth = scrollLeftButton.getWidth();
        panel.insert(scrollLeftButton, 0);
        panel.setWidgetLeftWidth(scrollLeftButton, 0, Unit.PX, leftImageWidth, Unit.PX);
        panel.setWidgetTopHeight(scrollLeftButton, 0, Unit.PX, scrollLeftButton.getHeight(), Unit.PX);

        scrollLeftButton.addClickHandler(createScrollClickHandler(SCROLL_INTERVAL));
        scrollLeftButton.setVisible(false);

        scrollRightButton = new Image(rightArrowImage);
        panel.insert(scrollRightButton, 0);
        panel.setWidgetLeftWidth(scrollRightButton, leftImageWidth + IMAGE_PADDING_PIXELS, Unit.PX,
                scrollRightButton.getWidth(), Unit.PX);
        panel.setWidgetTopHeight(scrollRightButton, 0, Unit.PX, scrollRightButton.getHeight(), Unit.PX);

        scrollRightButton.addClickHandler(createScrollClickHandler(-SCROLL_INTERVAL));
        scrollRightButton.setVisible(false);
    }

    private void showScrollButtonsIfNecessary()
    {
        // Defer size calculations until sizes are available.
        // When calculating immediately after add(), all size methods return zero.
        Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand()
        {
            @Override
            public void execute()
            {
                boolean isScrolling = isScrollingNecessary();
                // When the scroll buttons are being hidden, reset the scroll position to zero to
                // make sure no tabs are still out of sight
                if (scrollRightButton.isVisible() && !isScrolling)
                {
                    resetScrollPosition();
                }
                scrollRightButton.setVisible(isScrolling);
                scrollLeftButton.setVisible(isScrolling);
            }
        });
    }

    private void resetScrollPosition()
    {
        scrollTo(0);
    }

    private void scrollTo(int pos)
    {
        tabBar.getElement().getStyle().setLeft(pos, Unit.PX);
    }

    private boolean isScrollingNecessary()
    {
        Widget lastTab = getLastTab();
        return lastTab != null && getRightPosition(lastTab) > getTabBarWidth();
    }

    private int getRightPosition(Widget widget)
    {
        return widget.getElement().getOffsetLeft() + widget.getElement().getOffsetWidth();
    }

    private int getTabBarWidth()
    {
        return tabBar.getElement().getParentElement().getClientWidth();
    }

    private Widget getLastTab()
    {
        if (tabBar.getWidgetCount() == 0)
            return null;

        return tabBar.getWidget(tabBar.getWidgetCount() - 1);
    }

    private static int parsePosition(String positionString)
    {
        int position = 0;
        int sign = -1;
        int i = 0;
        if (positionString.charAt(0) == '-')
        {
            sign = -1;
            i++;
        }
        for (; i < positionString.length(); i++)
        {
            char c = positionString.charAt(i);
            if (c < '0' || c > '9')
                break;
            position = 10 * position + c - '0';
        }

        return sign * position;
    }
}

Thanks for the good work which saved me quite some time!

PS.: To sana ben aissa and others: "how to use it" => where you use TabLayoutPanel, just use ScrollableTabLayoutPanel.

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

No comments:

Post a Comment