/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores * CA 94065 USA or visit www.oracle.com if you need additional information or * have any questions. */ package com.sun.lwuit; import com.sun.lwuit.animations.Motion; import com.sun.lwuit.events.ActionEvent; import com.sun.lwuit.events.ActionListener; import com.sun.lwuit.events.FocusListener; import com.sun.lwuit.geom.Dimension; import com.sun.lwuit.layouts.BorderLayout; import com.sun.lwuit.layouts.BoxLayout; import com.sun.lwuit.layouts.FlowLayout; import com.sun.lwuit.layouts.GridLayout; import com.sun.lwuit.layouts.Layout; import com.sun.lwuit.plaf.Style; import com.sun.lwuit.plaf.UIManager; import com.sun.lwuit.util.EventDispatcher; /** * A component that lets the user switch between a group of components by * clicking on a tab with a given title and/or icon. * * <p> * Tabs/components are added to a <code>Tabs</code> object by using the * <code>addTab</code> and <code>insertTab</code> methods. * A tab is represented by an index corresponding * to the position it was added in, where the first tab has an index equal to 0 * and the last tab has an index equal to the tab count minus 1. * <p> * The <code>Tabs</code> uses a <code>SingleSelectionModel</code> * to represent the set of tab indices and the currently selected index. * If the tab count is greater than 0, then there will always be a selected * index, which by default will be initialized to the first tab. * If the tab count is 0, then the selected index will be -1. * <p> * * @author Chen Fishbein * */ public class Tabs extends Container { private Container contentPane = new Container(new TabsLayout()); /** * Where the tabs are placed. */ private int tabPlacement = -1; private Container tabsContainer; private ButtonGroup radioGroup = new ButtonGroup(); private Button selectedTab; private boolean swipeActivated = true; private ActionListener press, drag, release; private Motion slideToDestMotion; private int initialX = -1; private int initialY = -1; private int lastX = -1; private boolean dragStarted = false; private int activeComponent = 0; private EventDispatcher focusListeners; private TabFocusListener focusListener = new TabFocusListener(); private boolean tabsFillRows; private boolean tabsGridLayout; private int textPosition = -1; private boolean changeTabOnFocus; private boolean changeTabContainerStyleOnFocus; private Style originalTabsContainerUnselected, originalTabsContainerSelected; /** * Creates an empty <code>TabbedPane</code> with a default * tab placement of <code>Component.TOP</code>. */ public Tabs() { this(UIManager.getInstance().getThemeConstant("tabPlacementInt", TOP)); } /** * Creates an empty <code>TabbedPane</code> with the specified tab placement * of either: <code>Component.TOP</code>, <code>Component.BOTTOM</code>, * <code>Component.LEFT</code>, or <code>Component.RIGHT</code>. * * @param tabPlacement the placement for the tabs relative to the content */ public Tabs(int tabPlacement) { super(new BorderLayout()); contentPane.setUIID("TabbedPane"); super.addComponent(BorderLayout.CENTER, contentPane); tabsContainer = new Container(); tabsContainer.setUIID("TabsContainer"); tabsContainer.setScrollVisible(false); tabsContainer.getStyle().setMargin(0, 0, 0, 0); tabsFillRows = UIManager.getInstance().isThemeConstant("tabsFillRowsBool", false); tabsGridLayout = UIManager.getInstance().isThemeConstant("tabsGridBool", false); changeTabOnFocus = UIManager.getInstance().isThemeConstant("changeTabOnFocusBool", false); changeTabContainerStyleOnFocus = UIManager.getInstance().isThemeConstant("changeTabContainerStyleOnFocusBool", false); ((FlowLayout)tabsContainer.getLayout()).setFillRows(tabsFillRows); setTabPlacement(tabPlacement); press = new SwipeListener(SwipeListener.PRESS); drag = new SwipeListener(SwipeListener.DRAG); release = new SwipeListener(SwipeListener.RELEASE); setUIID("Tabs"); } /** * @inheritDoc */ void initComponentImpl() { super.initComponentImpl(); getComponentForm().registerAnimatedInternal(this); if(changeTabContainerStyleOnFocus && Display.getInstance().shouldRenderSelection()) { Component f = getComponentForm().getFocused(); if(f != null && f.getParent() == tabsContainer) { initTabsContainerStyle(); tabsContainer.setUnselectedStyle(originalTabsContainerSelected); tabsContainer.repaint(); } } } /** * @inheritDoc */ public void refreshTheme() { super.refreshTheme(); originalTabsContainerSelected = null; originalTabsContainerUnselected = null; } /** * @inheritDoc */ protected void deinitialize() { Form form = this.getComponentForm(); if (form != null) { form.removePointerPressedListener(press); form.removePointerReleasedListener(release); form.removePointerDraggedListener(drag); } super.deinitialize(); } /** * @inheritDoc */ protected void initComponent() { super.initComponent(); Form form = this.getComponentForm(); if (form != null && swipeActivated) { form.addPointerPressedListener(press); form.addPointerReleasedListener(release); form.addPointerDraggedListener(drag); } } /** * @inheritDoc */ public boolean animate() { boolean b = super.animate(); if (slideToDestMotion != null && swipeActivated) { int motionX = slideToDestMotion.getValue(); final int size = contentPane.getComponentCount(); for (int i = 0; i < size; i++) { int xOffset; if(isRTL()) { xOffset = (size - i) * contentPane.getWidth(); xOffset -= ((size - activeComponent) * contentPane.getWidth()); } else { xOffset = i * contentPane.getWidth(); xOffset -= (activeComponent * contentPane.getWidth()); } xOffset += motionX; Component component = contentPane.getComponentAt(i); component.setX(xOffset); } if (slideToDestMotion.isFinished()) { for (int i = 0; i < contentPane.getComponentCount() ; i++) { Component component = contentPane.getComponentAt(i); component.paintLockRelease(); } slideToDestMotion = null; deregisterAnimatedInternal(); setSelectedIndex(activeComponent); } return true; } return b; } void deregisterAnimatedInternal() { if (slideToDestMotion == null || (slideToDestMotion.isFinished())) { Form f = getComponentForm(); if (f != null) { f.deregisterAnimatedInternal(this); } } } /** * Sets the position of the text relative to the icon if exists * * @param textPosition alignment value (LEFT, RIGHT, BOTTOM or TOP) * @see #LEFT * @see #RIGHT * @see #BOTTOM * @see #TOP */ public void setTabTextPosition(int textPosition) { if (textPosition != LEFT && textPosition != RIGHT && textPosition != BOTTOM && textPosition != TOP) { throw new IllegalArgumentException("Text position can't be set to " + textPosition); } this.textPosition = textPosition; for(int iter = 0 ; iter < getTabCount() ; iter++) { Button b = (Button)tabsContainer.getComponentAt(iter); b.setTextPosition(textPosition); } } /** * Returns The position of the text relative to the icon * * @return The position of the text relative to the icon, one of: LEFT, RIGHT, BOTTOM, TOP * @see #LEFT * @see #RIGHT * @see #BOTTOM * @see #TOP */ public int getTabTextPosition(){ return textPosition; } /** * Sets the tab placement for this tabbedpane. * Possible values are:<ul> * <li><code>Component.TOP</code> * <li><code>Component.BOTTOM</code> * <li><code>Component.LEFT</code> * <li><code>Component.RIGHT</code> * </ul> * The default value, if not set, is <code>Component.TOP</code>. * * @param tabPlacement the placement for the tabs relative to the content */ public void setTabPlacement(int tabPlacement) { if (tabPlacement != TOP && tabPlacement != LEFT && tabPlacement != BOTTOM && tabPlacement != RIGHT) { throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT"); } if (this.tabPlacement == tabPlacement) { return; } this.tabPlacement = tabPlacement; removeComponent(tabsContainer); if (tabPlacement == TOP || tabPlacement == BOTTOM) { if(tabsFillRows) { FlowLayout f = new FlowLayout(); f.setFillRows(true); tabsContainer.setLayout(f); } else { if(tabsGridLayout) { tabsContainer.setLayout(new GridLayout(1, Math.max(1 ,getTabCount()))); } else { tabsContainer.setLayout(new BoxLayout(BoxLayout.X_AXIS)); } } tabsContainer.setScrollableX(true); tabsContainer.setScrollableY(false); if (tabPlacement == TOP) { super.addComponent(BorderLayout.NORTH, tabsContainer); } else if (tabPlacement == BOTTOM) { super.addComponent(BorderLayout.SOUTH, tabsContainer); } } else {// LEFT Or RIGHT tabsContainer.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); tabsContainer.setScrollableX(false); tabsContainer.setScrollableY(true); if (tabPlacement == LEFT) { super.addComponent(BorderLayout.WEST, tabsContainer); } else {// RIGHT super.addComponent(BorderLayout.EAST, tabsContainer); } } initTabsFocus(); tabsContainer.setShouldCalcPreferredSize(true); contentPane.setShouldCalcPreferredSize(true); revalidate(); } /** * Adds a <code>component</code> * represented by a <code>title</code> and/or <code>icon</code>, * either of which can be <code>null</code>. * Cover method for <code>insertTab</code>. * * @param title the title to be displayed in this tab * @param icon the icon to be displayed in this tab * @param component the component to be displayed when this tab is clicked * * @see #insertTab * @see #removeTabAt */ public void addTab(String title, Image icon, Component component) { insertTab(title, icon, component, tabsContainer.getComponentCount()); } /** * Adds a <code>component</code> * represented by a <code>title</code> and no <code>icon</code>. * Cover method for <code>insertTab</code>. * * @param title the title to be displayed in this tab * @param component the component to be displayed when this tab is clicked * * @see #insertTab * @see #removeTabAt */ public void addTab(String title, Component component) { insertTab(title, null, component, tabsContainer.getComponentCount()); } /** * Adds a <code>component</code> * represented by a <code>button</code>. * Cover method for <code>insertTab</code>. * The Button styling will be associated with "Tab" UIID. * * @param tab represents the tab on top * @param component the component to be displayed when this tab is clicked * * @see #insertTab * @see #removeTabAt */ public void addTab(Button tab, Component component) { insertTab(tab, component, tabsContainer.getComponentCount()); } /** * Inserts a <code>component</code>, at <code>index</code>, * represented by a <code>title</code> and/or <code>icon</code>, * either of which may be <code>null</code>. * Uses java.util.Vector internally, see <code>insertElementAt</code> * for details of insertion conventions. * * @param title the title to be displayed in this tab * @param icon the icon to be displayed in this tab * @param component The component to be displayed when this tab is clicked. * @param index the position to insert this new tab * * @see #addTab * @see #removeTabAt */ public void insertTab(String title, Image icon, Component component, int index) { RadioButton b = new RadioButton(title != null ? title : "", icon); b.setToggle(true); radioGroup.add(b); b.setTextPosition(BOTTOM); if(radioGroup.getButtonCount() == 1) { b.setSelected(true); } if(textPosition != -1) { b.setTextPosition(textPosition); } insertTab(b, component, index); } /** * Inserts a <code>component</code>, at <code>index</code>, * represented by a <code>button</code> * Uses java.util.Vector internally, see <code>insertElementAt</code> * for details of insertion conventions. * The Button styling will be associated with "Tab" UIID. * * @param tab represents the tab on top * @param component The component to be displayed when this tab is clicked. * @param index the position to insert this new tab * * @see #addTab * @see #removeTabAt */ public void insertTab(Button tab, Component component, int index) { checkIndex(index); if (component == null) { return; } final Button b = tab; b.setUIID("Tab"); if(b.getIcon() == null) { Image d = UIManager.getInstance().getThemeImageConstant("TabUnselectedImage"); if(d != null) { b.setIcon(d); d = UIManager.getInstance().getThemeImageConstant("TabSelectedImage"); if(d != null) { b.setRolloverIcon(d); b.setPressedIcon(d); } } } b.addFocusListener(focusListener); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if(selectedTab != null){ selectedTab.setUIID("Tab"); selectedTab.setShouldCalcPreferredSize(true); selectedTab.repaint(); int previousSelectedIndex = tabsContainer.getComponentIndex(selectedTab); // this might happen if a tab was removed if(previousSelectedIndex != -1) { Component previousContent = contentPane.getComponentAt(previousSelectedIndex); if (previousContent instanceof Container) { ((Container) previousContent).setBlockFocus(true); } } } activeComponent = tabsContainer.getComponentIndex(b); Component content = contentPane.getComponentAt(activeComponent); if (content instanceof Container) { ((Container) content).setBlockFocus(false); } initTabsFocus(); selectedTab = b; selectedTab.setShouldCalcPreferredSize(true); tabsContainer.revalidate(); tabsContainer.scrollComponentToVisible(selectedTab); contentPane.revalidate(); } }); if (component instanceof Container) { ((Container) component).setBlockFocus(true); } tabsContainer.addComponent(index, b); contentPane.addComponent(index, component); if(tabsGridLayout) { tabsContainer.setLayout(new GridLayout(1, Math.max(1 ,getTabCount()))); } if (tabsContainer.getComponentCount() == 1) { selectedTab = (Button) tabsContainer.getComponentAt(0); if (component instanceof Container) { ((Container) component).setBlockFocus(false); } initTabsFocus(); } } /** * Updates the information about the tab details * * @param title the title to be displayed in this tab * @param icon the icon to be displayed in this tab * @param index the position to insert this new tab */ public void setTabTitle(String title, Image icon, int index) { checkIndex(index); Button b = (Button)tabsContainer.getComponentAt(index); b.setText(title); b.setIcon(icon); } /** * Returns the title of the tab at the given index * * @param index index for the tab * @return label of the tab at the given index */ public String getTabTitle(int index) { checkIndex(index); return ((Button)tabsContainer.getComponentAt(index)).getText(); } /** * Returns the icon of the tab at the given index * * @param index index for the tab * @return icon of the tab at the given index */ public Image getTabIcon(int index) { checkIndex(index); return ((Button)tabsContainer.getComponentAt(index)).getIcon(); } /** * Returns the icon of the tab at the given index * * @param index index for the tab * @return icon of the tab at the given index */ public Image getTabSelectedIcon(int index) { checkIndex(index); return ((Button)tabsContainer.getComponentAt(index)).getPressedIcon(); } /** * Sets the selected icon of the tab at the given index * * @param index index for the tab * @param icon of the tab at the given index */ public void setTabSelectedIcon(int index, Image icon) { checkIndex(index); ((Button)tabsContainer.getComponentAt(index)).setPressedIcon(icon); } /** * Removes the tab at <code>index</code>. * After the component associated with <code>index</code> is removed, * its visibility is reset to true to ensure it will be visible * if added to other containers. * @param index the index of the tab to be removed * @exception IndexOutOfBoundsException if index is out of range * (index < 0 || index >= tab count) * * @see #addTab * @see #insertTab */ public void removeTabAt(int index) { checkIndex(index); Component key = tabsContainer.getComponentAt(index); tabsContainer.removeComponent(key); Component content = contentPane.getComponentAt(index); contentPane.removeComponent(content); if(tabsGridLayout) { tabsContainer.setLayout(new GridLayout(1, Math.max(1 ,getTabCount()))); } } /** * Returns the tab at <code>index</code>. * * @param index the index of the tab to be removed * @exception IndexOutOfBoundsException if index is out of range * (index < 0 || index >= tab count) * @return the component at the given tab location * @see #addTab * @see #insertTab */ public Component getTabComponentAt(int index) { checkIndex(index); return contentPane.getComponentAt(index); } private void checkIndex(int index) { if (index < 0 || index > tabsContainer.getComponentCount()) { throw new IndexOutOfBoundsException("Index: " + index); } } /** * Returns the index of the tab for the specified component. * Returns -1 if there is no tab for this component. * * @param component the component for the tab * @return the first tab which matches this component, or -1 * if there is no tab for this component */ public int indexOfComponent(Component component) { return contentPane.getComponentIndex(component); } /** * Returns the number of tabs in this <code>tabbedpane</code>. * * @return an integer specifying the number of tabbed pages */ public int getTabCount() { return tabsContainer.getComponentCount(); } /** * Returns the currently selected index for this tabbedpane. * Returns -1 if there is no currently selected tab. * * @return the index of the selected tab */ public int getSelectedIndex() { if(tabsContainer != null){ return activeComponent; } return -1; } /** * Returns the component associated with the tab at the given index * * @return the component is now showing in the tabbed pane */ public Component getSelectedComponent() { int i = getSelectedIndex(); if(i == -1) { return null; } return getTabComponentAt(i); } /** * Adds a focus listener to the tabs buttons * * @param listener FocusListener */ public void addTabsFocusListener(FocusListener listener){ if(focusListeners == null){ focusListeners = new EventDispatcher(); } focusListeners.addListener(listener); } /** * Removes a foucs Listener from the tabs buttons * * @param listener FocusListener */ public void removeTabsFocusListener(FocusListener listener){ if(focusListeners != null){ focusListeners.removeListener(listener); } } /** * @inheritDoc */ public String toString() { String className = getClass().getName(); className = className.substring(className.lastIndexOf('.') + 1); return className + "[x=" + getX() + " y=" + getY() + " width=" + getWidth() + " height=" + getHeight() + ", tab placement = " + tabPlacement + ", tab count = " + getTabCount() + ", selected index = " + getSelectedIndex() + "]"; } /** * Returns the placement of the tabs for this tabbedpane. * * @return the tab placement value * @see #setTabPlacement */ public int getTabPlacement() { return tabPlacement; } /** * This method retrieves the Tabs content pane * * @return the content pane Container */ public Container getContentPane(){ return contentPane; } /** * This method retrieves the Tabs buttons Container * * @return the Tabs Container */ public Container getTabsContainer(){ return tabsContainer; } /** * Sets the selected index for this tabbedpane. The index must be a valid * tab index. * @param index the index to be selected * @throws IndexOutOfBoundsException if index is out of range * (index < 0 || index >= tab count) */ public void setSelectedIndex(int index) { if (index < 0 || index >= tabsContainer.getComponentCount()) { throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+tabsContainer.getComponentCount()); } Button b = (Button)tabsContainer.getComponentAt(index); b.fireClicked(); } /** * Hide the tabs bar */ public void hideTabs(){ removeComponent(tabsContainer); revalidate(); } /** * Show the tabs bar if it was hidden */ public void showTabs(){ int tp = tabPlacement; tabPlacement = -1; setTabPlacement(tp); revalidate(); } /** * Returns true if the swipe between tabs is activated, this is relevant for * touch devices only * * @return swipe activated flag */ public boolean isSwipeActivated() { return swipeActivated; } /** * Setter method for swipe mode * * @param swipeActivated */ public void setSwipeActivated(boolean swipeActivated) { this.swipeActivated = swipeActivated; } private void initTabsFocus(){ for (int i = 0; i < tabsContainer.getComponentCount(); i++) { initTabFocus(tabsContainer.getComponentAt(i), contentPane.getComponentAt(activeComponent)); } } private void initTabFocus(Component tab, Component content) { Component focus = null; if (content.isFocusable()) { focus = content; } if (content instanceof Container) { focus = ((Container) content).findFirstFocusable(); } } /** * Indicates that a tab should change when the focus changes without the user physically pressing a button * @return the changeTabOnFocus */ public boolean isChangeTabOnFocus() { return changeTabOnFocus; } /** * Indicates that a tab should change when the focus changes without the user physically pressing a button * @param changeTabOnFocus the changeTabOnFocus to set */ public void setChangeTabOnFocus(boolean changeTabOnFocus) { this.changeTabOnFocus = changeTabOnFocus; } /** * Indicates that the tabs container should have its style changed to the selected style when one of the tabs has focus * this allows incorporating it into the theme of the application * @return the changeTabContainerStyleOnFocus */ public boolean isChangeTabContainerStyleOnFocus() { return changeTabContainerStyleOnFocus; } /** * Indicates that the tabs container should have its style changed to the selected style when one of the tabs has focus * this allows incorporating it into the theme of the application * @param changeTabContainerStyleOnFocus the changeTabContainerStyleOnFocus to set */ public void setChangeTabContainerStyleOnFocus(boolean changeTabContainerStyleOnFocus) { this.changeTabContainerStyleOnFocus = changeTabContainerStyleOnFocus; } class TabsLayout extends Layout{ public void layoutContainer(Container parent) { final int size = parent.getComponentCount(); for (int i = 0; i < size; i++) { int xOffset; if(isRTL()) { xOffset = (size - i) * parent.getWidth(); xOffset -= ((size - activeComponent) * parent.getWidth()); } else { xOffset = i * parent.getWidth(); xOffset -= (activeComponent * parent.getWidth()); } Component component = parent.getComponentAt(i); component.setX(component.getStyle().getMargin(Component.LEFT) + xOffset); component.setY(component.getStyle().getMargin(Component.TOP)); component.setWidth(parent.getWidth() - component.getStyle().getMargin(Component.LEFT) - component.getStyle().getMargin(Component.RIGHT)); component.setHeight(parent.getHeight() - component.getStyle().getMargin(Component.TOP) - component.getStyle().getMargin(Component.BOTTOM)); } } public Dimension getPreferredSize(Container parent) { // fill Dimension dim = new Dimension(0, 0); dim.setWidth(parent.getWidth() + parent.getStyle().getPadding(false, Component.LEFT) + parent.getStyle().getPadding(false, Component.RIGHT)); dim.setHeight(parent.getHeight() + parent.getStyle().getPadding(false, Component.TOP) + parent.getStyle().getPadding(false, Component.BOTTOM)); int compCount = contentPane.getComponentCount(); for(int iter = 0 ; iter < compCount ; iter++) { Dimension d = contentPane.getComponentAt(iter).getPreferredSizeWithMargin(); dim.setWidth(Math.max(d.getWidth(), dim.getWidth())); dim.setHeight(Math.max(d.getHeight(), dim.getHeight())); } return dim; } } void initTabsContainerStyle() { if(originalTabsContainerSelected == null) { originalTabsContainerSelected = tabsContainer.getSelectedStyle(); originalTabsContainerUnselected = tabsContainer.getUnselectedStyle(); } } class TabFocusListener implements FocusListener{ public void focusGained(Component cmp) { if(focusListeners != null){ focusListeners.fireFocus(cmp); } if(Display.getInstance().shouldRenderSelection()) { if(isChangeTabOnFocus()) { if(!((Button)cmp).isSelected()) { ((Button)cmp).fireClicked(); } } if(changeTabContainerStyleOnFocus) { initTabsContainerStyle(); tabsContainer.setUnselectedStyle(originalTabsContainerSelected); tabsContainer.repaint(); } } } public void focusLost(Component cmp) { if(focusListeners != null){ focusListeners.fireFocus(cmp); } if(changeTabContainerStyleOnFocus) { initTabsContainerStyle(); tabsContainer.setUnselectedStyle(originalTabsContainerUnselected); tabsContainer.repaint(); } } } class SwipeListener implements ActionListener{ private final static int PRESS = 0; private final static int DRAG = 1; private final static int RELEASE = 2; private final int type; private boolean blockSwipe; private boolean riskySwipe; SwipeListener(int type) { this.type = type; } public void actionPerformed(ActionEvent evt) { if (getComponentCount() == 0 || !swipeActivated ||animate()) { return; } final int x = evt.getX(); final int y = evt.getY(); switch (type) { case PRESS: { blockSwipe = false; riskySwipe = false; if (contentPane.contains(x, y)) { Component testCmp = contentPane.getComponentAt(x, y); if(testCmp != null && testCmp != contentPane) { while(testCmp != null && testCmp != contentPane) { if(testCmp.shouldBlockSideSwipe()) { lastX = -1; initialX = -1; blockSwipe = true; return; } if(testCmp.isScrollable()) { if(testCmp.isScrollableX()) { // we need to block swipe since the user is trying to scroll a component lastX = -1; initialX = -1; blockSwipe = true; return; } // scrollable Y component, we want to make side scrolling // slightly harder so it doesn't bother the vertical swipe riskySwipe = true; break; } testCmp = testCmp.getParent(); } } lastX = x; initialX = x; initialY = y; } else { lastX = -1; initialX = -1; initialY = -1; } dragStarted = false; break; } case DRAG: { if(blockSwipe) { return; } if (!dragStarted) { if(riskySwipe) { if(Math.abs(x - initialX) < Math.abs(y - initialY)) { return; } // give heavier weight when we have two axis swipe dragStarted = Math.abs(x - initialX) > (contentPane.getWidth() / 5); } else { // start drag not imediately, giving components some sort // of weight. dragStarted = Math.abs(x - initialX) > (contentPane.getWidth() / 8); } } if (initialX != -1 && contentPane.contains(x, y)) { int diffX = x - lastX; if (diffX != 0 && dragStarted) { lastX += diffX; final int size = contentPane.getComponentCount(); for (int i = 0; i < size; i++) { Component component = contentPane.getComponentAt(i); component.setX(component.getX() + diffX); component.paintLock(false); } repaint(); } } break; } case RELEASE: { if(changeTabContainerStyleOnFocus) { initTabsContainerStyle(); tabsContainer.setUnselectedStyle(originalTabsContainerUnselected); tabsContainer.repaint(); } if(blockSwipe) { return; } if (initialX != -1) { int diff = x - initialX; if (diff != 0 && dragStarted) { if (Math.abs(diff) > contentPane.getWidth() / 2) { if(isRTL()) { diff *= -1; } if (diff > 0) { activeComponent--; if (activeComponent < 0) { activeComponent = activeComponent = 0; } } else { activeComponent++; if (activeComponent >= contentPane.getComponentCount()) { activeComponent = contentPane.getComponentCount() - 1; } } } int start = contentPane.getComponentAt(activeComponent).getX(); int end = 0; slideToDestMotion = Motion.createSplineMotion(start, end, 250); slideToDestMotion.start(); Form form = getComponentForm(); if (form != null) { form.registerAnimatedInternal(Tabs.this); } } } lastX = -1; initialX = -1; dragStarted = false; break; } } } } /** * @inheritDoc */ public String[] getPropertyNames() { return new String[] {"titles", "icons", "selectedIcons"}; } /** * @inheritDoc */ public Class[] getPropertyTypes() { return new Class[] {String[].class, Image[].class, Image[].class}; } /** * @inheritDoc */ public Object getPropertyValue(String name) { if(name.equals("titles")) { String[] t = new String[getTabCount()]; for(int iter = 0 ; iter < t.length ; iter++) { t[iter] = getTabTitle(iter); } return t; } if(name.equals("icons")) { Image[] t = new Image[getTabCount()]; for(int iter = 0 ; iter < t.length ; iter++) { t[iter] = getTabIcon(iter); } return t; } if(name.equals("selectedIcons")) { Image[] t = new Image[getTabCount()]; for(int iter = 0 ; iter < t.length ; iter++) { t[iter] = getTabSelectedIcon(iter); } return t; } return null; } /** * @inheritDoc */ public String setPropertyValue(String name, Object value) { if(name.equals("titles")) { String[] t = (String[])value; for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) { setTabTitle(t[iter], getTabIcon(iter), iter); } return null; } if(name.equals("icons")) { Image[] t = (Image[])value; for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) { setTabTitle(getTabTitle(iter), t[iter], iter); } return null; } if(name.equals("selectedIcons")) { Image[] t = (Image[])value; for(int iter = 0 ; iter < Math.min(getTabCount(), t.length) ; iter++) { setTabSelectedIcon(iter, t[iter]); } return null; } return super.setPropertyValue(name, value); } }