summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2014-08-19 09:40:20 +0300
committerTeemu Suo-Anttila <teemusa@vaadin.com>2014-08-19 09:42:27 +0300
commitc1a873bc9e47b98c58b298aa6935ab0853e6963f (patch)
tree3e8b209c54961473161335159bc98f9316d28a09 /client
parent2caaea2df9d558f0ce67daa3b0641cb832538506 (diff)
parent4dcace7123b605115efcbb395a320d460eed9c0e (diff)
downloadvaadin-framework-c1a873bc9e47b98c58b298aa6935ab0853e6963f.tar.gz
vaadin-framework-c1a873bc9e47b98c58b298aa6935ab0853e6963f.zip
Merge remote-tracking branch 'origin/master' into grid
Change-Id: Iac6947bc82bfbbb6856a924e7d538d195cfb405e
Diffstat (limited to 'client')
-rwxr-xr-xclient/src/com/vaadin/DefaultWidgetSet.gwt.xml1
-rw-r--r--client/src/com/vaadin/Vaadin.gwt.xml2
-rw-r--r--client/src/com/vaadin/client/Util.java34
-rw-r--r--client/src/com/vaadin/client/ui/VDragAndDropWrapper.java27
-rw-r--r--client/src/com/vaadin/client/ui/VFilterSelect.java51
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheet.java727
-rw-r--r--client/src/com/vaadin/client/ui/VWindow.java14
-rw-r--r--client/src/com/vaadin/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java26
-rw-r--r--client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java11
9 files changed, 723 insertions, 170 deletions
diff --git a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
index 2719493853..8512d547e3 100755
--- a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
+++ b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml
@@ -14,4 +14,5 @@
file. Speeds up compilation and does not make the Javascript significantly
larger. -->
<collapse-all-properties />
+
</module>
diff --git a/client/src/com/vaadin/Vaadin.gwt.xml b/client/src/com/vaadin/Vaadin.gwt.xml
index 711729f64f..aad0563975 100644
--- a/client/src/com/vaadin/Vaadin.gwt.xml
+++ b/client/src/com/vaadin/Vaadin.gwt.xml
@@ -68,7 +68,7 @@
<property-provider name="modernie"><![CDATA[
{
var ua = $wnd.navigator.userAgent;
- if (ua.indexOf('IE') == -1 && ua.indexOf('Trident') != -1) { return 'yes'; }
+ if (ua.indexOf('MSIE') == -1 && ua.indexOf('Trident') != -1) { return 'yes'; }
return 'none';
}
]]></property-provider>
diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java
index 49c862006b..e37b044826 100644
--- a/client/src/com/vaadin/client/Util.java
+++ b/client/src/com/vaadin/client/Util.java
@@ -1420,6 +1420,37 @@ public class Util {
}
/**
+ * Sets the selection range of an input element.
+ *
+ * We need this JSNI function to set selection range so that we can use the
+ * optional direction attribute to set the anchor to the end and the focus
+ * to the start. This makes Firefox work the same way as other browsers
+ * (#13477)
+ *
+ * @param elem
+ * the html input element.
+ * @param pos
+ * the index of the first selected character.
+ * @param length
+ * the selection length.
+ * @param direction
+ * a string indicating the direction in which the selection was
+ * performed. This may be "forward" or "backward", or "none" if
+ * the direction is unknown or irrelevant.
+ *
+ * @since
+ */
+ public native static void setSelectionRange(Element elem, int pos,
+ int length, String direction)
+ /*-{
+ try {
+ elem.setSelectionRange(pos, pos + length, direction);
+ } catch (e) {
+ // Firefox throws exception if TextBox is not visible, even if attached
+ }
+ }-*/;
+
+ /**
* Wrap a css size value and its unit and translate back and forth to the
* string representation.<br/>
* Eg. 50%, 123px, ...
@@ -1571,7 +1602,8 @@ public class Util {
* @return true if the two sizes are equals, otherwise false.
*/
public static boolean equals(String cssSize1, String cssSize2) {
- return CssSize.fromString(cssSize1).equals(CssSize.fromString(cssSize2));
+ return CssSize.fromString(cssSize1).equals(
+ CssSize.fromString(cssSize2));
}
}
diff --git a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
index 4010ffd542..7bf341a387 100644
--- a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
+++ b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java
@@ -114,7 +114,8 @@ public class VDragAndDropWrapper extends VCustomComponent implements
* @return true if the event was handled as a drag start event
*/
private boolean startDrag(NativeEvent event) {
- if (dragStartMode == WRAPPER || dragStartMode == COMPONENT) {
+ if (dragStartMode == WRAPPER || dragStartMode == COMPONENT
+ || dragStartMode == COMPONENT_OTHER) {
VTransferable transferable = new VTransferable();
transferable.setDragSource(getConnector());
@@ -130,6 +131,10 @@ public class VDragAndDropWrapper extends VCustomComponent implements
if (dragStartMode == WRAPPER) {
dragEvent.createDragImage(getElement(), true);
+ } else if (dragStartMode == COMPONENT_OTHER
+ && getDragImageWidget() != null) {
+ dragEvent.createDragImage(getDragImageWidget().getElement(),
+ true);
} else {
dragEvent.createDragImage(widget.getElement(), true);
}
@@ -142,6 +147,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
protected final static int COMPONENT = 1;
protected final static int WRAPPER = 2;
protected final static int HTML5 = 3;
+ protected final static int COMPONENT_OTHER = 4;
/** For internal use only. May be removed or replaced in the future. */
public int dragStartMode;
@@ -458,6 +464,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements
* Flag used by html5 dd
*/
private boolean currentlyValid;
+ private Widget dragImageWidget;
private static final String OVER_STYLE = "v-ddwrapper-over";
@@ -661,4 +668,22 @@ public class VDragAndDropWrapper extends VCustomComponent implements
notifySizePotentiallyChanged();
}
+ /**
+ * Set the widget that will be used as the drag image when using
+ * DragStartMode {@link COMPONENT_OTHER} .
+ *
+ * @param widget
+ */
+ public void setDragAndDropWidget(Widget widget) {
+ dragImageWidget = widget;
+ }
+
+ /**
+ * @return the widget used as drag image. Returns <code>null</code> if no
+ * widget is set.
+ */
+ public Widget getDragImageWidget() {
+ return dragImageWidget;
+ }
+
}
diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java
index 6ba0785acc..230c9e6639 100644
--- a/client/src/com/vaadin/client/ui/VFilterSelect.java
+++ b/client/src/com/vaadin/client/ui/VFilterSelect.java
@@ -254,7 +254,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Shows the popup where the user can see the filtered options
- *
+ *
* @param currentSuggestions
* The filtered suggestions
* @param currentPage
@@ -345,7 +345,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Should the next page button be visible to the user?
- *
+ *
* @param active
*/
private void setNextButtonActive(boolean active) {
@@ -365,7 +365,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Should the previous page button be visible to the user
- *
+ *
* @param active
*/
private void setPrevButtonActive(boolean active) {
@@ -554,7 +554,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* amount of items are visible at a time and a scrollbar or buttons are
* visible to change page. If paging is turned of then all options are
* rendered into the popup menu.
- *
+ *
* @param paging
* Should the paging be turned on?
*/
@@ -679,7 +679,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Was the popup just closed?
- *
+ *
* @return true if popup was just closed
*/
public boolean isJustClosed() {
@@ -708,7 +708,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Updates style names in suggestion popup to help theme building.
- *
+ *
* @param uidl
* UIDL for the whole combo box
* @param componentState
@@ -799,7 +799,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
/**
* Sets the suggestions rendered in the menu
- *
+ *
* @param suggestions
* The suggestions to be rendered in the menu
*/
@@ -1057,11 +1057,33 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
@Override
public void setSelectionRange(int pos, int length) {
if (textInputEnabled) {
- super.setSelectionRange(pos, length);
+ /*
+ * set selection range with a backwards direction: anchor at the
+ * back, focus at the front. This means that items that are too
+ * long to display will display from the start and not the end
+ * even on Firefox.
+ *
+ * We need the JSNI function to set selection range so that we
+ * can use the optional direction attribute to set the anchor to
+ * the end and the focus to the start. This makes Firefox work
+ * the same way as other browsers (#13477)
+ */
+ Util.setSelectionRange(getElement(), pos, length, "backward");
+
} else {
- super.setSelectionRange(0, getValue().length());
+ /*
+ * Setting the selectionrange for an uneditable textbox leads to
+ * unwanted behaviour when the width of the textbox is narrower
+ * than the width of the entry: the end of the entry is shown
+ * instead of the beginning. (see #13477)
+ *
+ * To avoid this, we set the caret to the beginning of the line.
+ */
+
+ super.setSelectionRange(0, 0);
}
}
+
}
@Deprecated
@@ -1456,9 +1478,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
private void setText(final String text) {
/**
- * To leave caret in the beginning of the line.
- * SetSelectionRange wouldn't work on IE
- * (see #13477)
+ * To leave caret in the beginning of the line. SetSelectionRange
+ * wouldn't work on IE (see #13477)
*/
Direction previousDirection = tb.getDirection();
tb.setDirection(Direction.RTL);
@@ -1763,10 +1784,14 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
if (!allowNewItem) {
/*
* New items are not allowed: If there is only one
- * suggestion, select that. Otherwise do nothing.
+ * suggestion, select that. If there is more than one
+ * suggestion Enter key should work as Escape key. Otherwise
+ * do nothing.
*/
if (currentSuggestions.size() == 1) {
onSuggestionSelected(currentSuggestions.get(0));
+ } else if (currentSuggestions.size() > 1) {
+ reset();
}
} else {
// Handle addition of new items.
diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java
index 15d9c83c49..aff1848647 100644
--- a/client/src/com/vaadin/client/ui/VTabsheet.java
+++ b/client/src/com/vaadin/client/ui/VTabsheet.java
@@ -1,12 +1,12 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
- *
+ *
* 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
@@ -42,9 +42,12 @@ import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasBlurHandlers;
import com.google.gwt.event.dom.client.HasFocusHandlers;
import com.google.gwt.event.dom.client.HasKeyDownHandlers;
+import com.google.gwt.event.dom.client.HasMouseDownHandlers;
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.dom.client.MouseDownEvent;
+import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
@@ -74,7 +77,9 @@ import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc;
import com.vaadin.shared.ui.tabsheet.TabsheetState;
public class VTabsheet extends VTabsheetBase implements Focusable,
- FocusHandler, BlurHandler, KeyDownHandler, SubPartAware {
+ SubPartAware,
+ // TODO: These listeners are due to be removed in 7.3
+ FocusHandler, BlurHandler, KeyDownHandler {
private static class VCloseEvent {
private Tab tab;
@@ -95,10 +100,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Representation of a single "tab" shown in the TabBar
- *
+ *
*/
public static class Tab extends SimplePanel implements HasFocusHandlers,
- HasBlurHandlers, HasKeyDownHandlers {
+ HasBlurHandlers, HasMouseDownHandlers, HasKeyDownHandlers {
private static final String TD_CLASSNAME = CLASSNAME + "-tabitemcell";
private static final String TD_FIRST_CLASSNAME = TD_CLASSNAME
+ "-first";
@@ -152,10 +157,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
Roles.getTabRole().setAriaLabelledbyProperty(getElement(),
Id.of(tabCaption.getElement()));
-
- addFocusHandler(getTabsheet());
- addBlurHandler(getTabsheet());
- addKeyDownHandler(getTabsheet());
}
public boolean isHiddenOnServer() {
@@ -197,7 +198,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Toggles the style names for the Tab
- *
+ *
* @param selected
* true if the Tab is selected
* @param first
@@ -282,6 +283,11 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
@Override
+ public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
+ return addDomHandler(handler, MouseDownEvent.getType());
+ }
+
+ @Override
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return addDomHandler(handler, KeyDownEvent.getType());
}
@@ -420,8 +426,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
- static class TabBar extends ComplexPanel implements ClickHandler,
- VCloseHandler {
+ static class TabBar extends ComplexPanel implements VCloseHandler {
private final Element tr = DOM.createTR();
@@ -443,6 +448,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
setStyleName(spacerTd, CLASSNAME + "-spacertd");
DOM.appendChild(tr, spacerTd);
DOM.appendChild(spacerTd, DOM.createDiv());
+
setElement(el);
}
@@ -460,10 +466,20 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return DOM.asOld(tr);
}
+ /**
+ * Gets the number of tabs from the tab bar.
+ *
+ * @return the number of tabs from the tab bar.
+ */
public int getTabCount() {
return getWidgetCount();
}
+ /**
+ * Adds a tab to the tab bar.
+ *
+ * @return the added tab.
+ */
public Tab addTab() {
Tab t = new Tab(this);
int tabIndex = getTabCount();
@@ -476,31 +492,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
t.setStyleNames(false, true);
}
- t.addClickHandler(this);
+ getTabsheet().selectionHandler.registerTab(t);
+
t.setCloseHandler(this);
return t;
}
- @Override
- public void onClick(ClickEvent event) {
- TabCaption caption = (TabCaption) event.getSource();
- Element targetElement = event.getNativeEvent().getEventTarget()
- .cast();
- // the tab should not be focused if the close button was clicked
- if (targetElement == caption.getCloseButton()) {
- return;
- }
-
- int index = getWidgetIndex(caption.getParent());
-
- navigateTab(getTabsheet().focusedTabIndex, index);
- getTabsheet().focusedTabIndex = index;
- getTabsheet().focusedTab = getTab(index);
- getTabsheet().focus();
- getTabsheet().loadTabSheet(index);
- }
-
+ /**
+ * Gets the tab sheet instance where the tab bar is attached to.
+ *
+ * @return the tab sheet instance where the tab bar is attached to.
+ */
public VTabsheet getTabsheet() {
return tabsheet;
}
@@ -538,7 +541,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
getTab(tabsheet.activeTabIndex).recalculateCaptionWidth();
}
- public void navigateTab(int fromIndex, int toIndex) {
+ public Tab navigateTab(int fromIndex, int toIndex) {
Tab newNavigated = getTab(toIndex);
if (newNavigated == null) {
throw new IllegalArgumentException(
@@ -553,6 +556,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
oldNavigated.setStyleNames(oldNavigated.equals(selected),
isFirstVisibleTab(fromIndex), false);
}
+
+ return newNavigated;
}
public void removeTab(int i) {
@@ -580,7 +585,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Returns the index of the first visible tab
- *
+ *
* @return
*/
private int getFirstVisibleTab() {
@@ -589,7 +594,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Find the next visible tab. Returns -1 if none is found.
- *
+ *
* @param i
* @return
*/
@@ -608,7 +613,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Find the previous visible tab. Returns -1 if none is found.
- *
+ *
* @param i
* @return
*/
@@ -663,7 +668,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/** For internal use only. May be removed or replaced in the future. */
// tabbar and 'scroller' container
public final Element tabs;
- Tab focusedTab;
+
/**
* The tabindex property (position in the browser's focus cycle.) Named like
* this to avoid confusion with activeTabIndex.
@@ -697,9 +702,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private String currentStyle;
- /** For internal use only. May be removed or replaced in the future. */
- private int focusedTabIndex = 0;
-
/**
* @return Whether the tab could be selected or not.
*/
@@ -720,11 +722,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Load the content of a tab of the provided index.
- *
+ *
* @param index
* of the tab to load
+ *
+ * @return true if the specified sheet gets loaded, otherwise false.
*/
- public void loadTabSheet(int tabIndex) {
+ public boolean loadTabSheet(int tabIndex) {
if (activeTabIndex != tabIndex && canSelectTab(tabIndex)) {
tb.selectTab(tabIndex);
@@ -741,12 +745,16 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
waitingForResponse = true;
tb.getTab(tabIndex).focus(); // move keyboard focus to active tab
+
+ return true;
}
+
+ return false;
}
/**
* Returns the currently displayed widget in the tab panel.
- *
+ *
* @since 7.2
* @return currently displayed content widget
*/
@@ -756,7 +764,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Returns the client to server RPC proxy for the tabsheet.
- *
+ *
* @since 7.2
* @return RPC proxy
*/
@@ -766,10 +774,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* For internal use only.
- *
+ *
* Avoid using this method directly and use appropriate superclass methods
* where applicable.
- *
+ *
* @deprecated since 7.2 - use more specific methods instead (getRpcProxy(),
* getConnectorForWidget(Widget) etc.)
* @return ApplicationConnection
@@ -799,9 +807,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public VTabsheet() {
super(CLASSNAME);
- addHandler(this, FocusEvent.getType());
- addHandler(this, BlurEvent.getType());
-
// Tab scrolling
getElement().getStyle().setOverflow(Overflow.HIDDEN);
tabs = DOM.createDiv();
@@ -812,18 +817,21 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
Roles.getTablistRole().setAriaHiddenState(scroller, true);
DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME);
+
scrollerPrev = DOM.createButton();
scrollerPrev.setTabIndex(-1);
DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME
+ "Prev");
Roles.getTablistRole().setAriaHiddenState(scrollerPrev, true);
- DOM.sinkEvents(scrollerPrev, Event.ONCLICK);
+ DOM.sinkEvents(scrollerPrev, Event.ONCLICK | Event.ONMOUSEDOWN);
+
scrollerNext = DOM.createButton();
scrollerNext.setTabIndex(-1);
DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME
+ "Next");
Roles.getTablistRole().setAriaHiddenState(scrollerNext, true);
- DOM.sinkEvents(scrollerNext, Event.ONCLICK);
+ DOM.sinkEvents(scrollerNext, Event.ONCLICK | Event.ONMOUSEDOWN);
+
DOM.appendChild(getElement(), tabs);
// Tabs
@@ -856,35 +864,68 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
@Override
public void onBrowserEvent(Event event) {
+ com.google.gwt.dom.client.Element eventTarget = DOM
+ .eventGetTarget(event);
+
if (event.getTypeInt() == Event.ONCLICK) {
+
// Tab scrolling
- if (isScrolledTabs() && DOM.eventGetTarget(event) == scrollerPrev) {
- int newFirstIndex = tb.scrollLeft(scrollerIndex);
- if (newFirstIndex != -1) {
- scrollerIndex = newFirstIndex;
- updateTabScroller();
- }
- event.stopPropagation();
- return;
- } else if (isClippedTabs()
- && DOM.eventGetTarget(event) == scrollerNext) {
- int newFirstIndex = tb.scrollRight(scrollerIndex);
+ if (eventTarget == scrollerPrev || eventTarget == scrollerNext) {
+ scrollAccordingToScrollTarget(eventTarget);
- if (newFirstIndex != -1) {
- scrollerIndex = newFirstIndex;
- updateTabScroller();
- }
event.stopPropagation();
+ }
+
+ } else if (event.getTypeInt() == Event.ONMOUSEDOWN) {
+
+ if (eventTarget == scrollerPrev || eventTarget == scrollerNext) {
+ // In case the focus was previously on a Tab, we need to cancel
+ // the upcoming blur on the Tab which will follow this mouse
+ // down event.
+ focusBlurManager.cancelNextBlurSchedule();
+
return;
}
}
+
super.onBrowserEvent(event);
}
+ /*
+ * Scroll the tab bar according to the last scrollTarget (the scroll button
+ * pressed).
+ */
+ private void scrollAccordingToScrollTarget(
+ com.google.gwt.dom.client.Element scrollTarget) {
+ if (scrollTarget == null) {
+ return;
+ }
+
+ int newFirstIndex = -1;
+
+ // Scroll left.
+ if (isScrolledTabs() && scrollTarget == scrollerPrev) {
+ newFirstIndex = tb.scrollLeft(scrollerIndex);
+
+ // Scroll right.
+ } else if (isClippedTabs() && scrollTarget == scrollerNext) {
+ newFirstIndex = tb.scrollRight(scrollerIndex);
+ }
+
+ if (newFirstIndex != -1) {
+ scrollerIndex = newFirstIndex;
+ updateTabScroller();
+ }
+
+ // For this to work well, make sure the method gets called only from
+ // user events.
+ selectionHandler.focusTabAtIndex(scrollerIndex);
+ }
+
/**
* Checks if the tab with the selected index has been scrolled out of the
* view (on the left side).
- *
+ *
* @param index
* @return
*/
@@ -1016,7 +1057,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Renders the widget content for a tab sheet.
- *
+ *
* @param newWidget
*/
public void renderContent(Widget newWidget) {
@@ -1239,64 +1280,467 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
@Override
- public void onBlur(BlurEvent event) {
- getVTooltip().hideTooltip();
+ public void focus() {
+ getActiveTab().focus();
+ }
- if (focusedTab != null && focusedTab == event.getSource()) {
- focusedTab.removeAssistiveDescription();
- focusedTab = null;
- if (connector.hasEventListener(EventId.BLUR)) {
- connector.getRpcProxy(FocusAndBlurServerRpc.class).blur();
- }
- }
+ public void blur() {
+ getActiveTab().blur();
+ }
+
+ /*
+ * Gets the active tab.
+ */
+ private Tab getActiveTab() {
+ return tb.getTab(activeTabIndex);
}
@Override
- public void onFocus(FocusEvent event) {
- if (focusedTab == null && event.getSource() instanceof Tab) {
- focusedTab = (Tab) event.getSource();
+ public void setConnector(AbstractComponentConnector connector) {
+ super.setConnector(connector);
+
+ focusBlurManager.connector = connector;
+ }
+
+ /*
+ * The focus and blur manager instance.
+ */
+ private FocusBlurManager focusBlurManager = new FocusBlurManager();
+
+ /*
+ * Generate the correct focus/blur events for the main TabSheet component
+ * (#14304).
+ *
+ * The TabSheet must fire one focus event when the user clicks on the tab
+ * bar (i.e. inner TabBar class) containing the Tabs or when the focus is
+ * provided to the TabSheet by any means. Also one blur event should be
+ * fired only when the user leaves the tab bar. After the user focus on the
+ * tab bar and before leaving it, no matter how many times he's pressing the
+ * Tabs or the scroll buttons, the TabSheet component should not fire any of
+ * those blur/focus events.
+ *
+ * The only focusable elements contained in the tab bar are the Tabs (see
+ * inner class Tab). The reason is the accessibility support.
+ *
+ * Having this in mind, the chosen solution path for our problem is to match
+ * a sequence of focus/blur events on the tabs, choose only the first focus
+ * and last blur events and pass only those further to the main component.
+ * Any consecutive blur/focus events on 2 Tabs must be ignored.
+ *
+ * Because in a blur event we don't know whether or not a focus will follow,
+ * we just defer a command initiated on the blur event to wait and see if
+ * any focus will appear. The command will be executed after the next focus,
+ * so if no focus was triggered in the mean while it'll submit the blur
+ * event to the main component, otherwise it'll do nothing, so the main
+ * component will not generate the blur..
+ */
+ private static class FocusBlurManager {
+
+ // The real tab with focus on it. If the focus goes to another element
+ // in the page this will be null.
+ private Tab focusedTab;
+
+ /*
+ * Gets the focused tab.
+ */
+ private Tab getFocusedTab() {
+ return focusedTab;
+ }
+
+ /*
+ * Sets the local field tracking the focused tab.
+ */
+ private void setFocusedTab(Tab focusedTab) {
+ this.focusedTab = focusedTab;
+ }
+
+ /*
+ * The ultimate focus/blur event dispatcher.
+ */
+ private AbstractComponentConnector connector;
+
+ /**
+ * Delegate method for the onFocus event occurring on Tab.
+ *
+ * @since
+ * @param newFocusTab
+ * the new focused tab.
+ * @see #onBlur(Tab)
+ */
+ public void onFocus(Tab newFocusTab) {
+
if (connector.hasEventListener(EventId.FOCUS)) {
- connector.getRpcProxy(FocusAndBlurServerRpc.class).focus();
+
+ // Send the focus event only first time when we focus on any
+ // tab. The focused tab will be reseted on the last blur.
+ if (focusedTab == null) {
+ connector.getRpcProxy(FocusAndBlurServerRpc.class).focus();
+ }
+ }
+
+ cancelLastBlurSchedule();
+
+ setFocusedTab(newFocusTab);
+ }
+
+ /**
+ * Delegate method for the onBlur event occurring on Tab.
+ *
+ * @param blurSource
+ * the source of the blur.
+ *
+ * @see #onFocus(Tab)
+ */
+ public void onBlur(Tab blurSource) {
+ if (focusedTab != null && focusedTab == blurSource) {
+
+ if (connector.hasEventListener(EventId.BLUR)) {
+ scheduleBlur(focusedTab);
+ }
+ }
+ }
+
+ /*
+ * The last blur command to be executed.
+ */
+ private BlurCommand blurCommand;
+
+ /*
+ * Execute the final blur command.
+ */
+ private class BlurCommand implements Command {
+
+ /*
+ * The blur source.
+ */
+ private Tab blurSource;
+
+ /**
+ * Create the blur command using the blur source.
+ *
+ * @param blurSource
+ * the source.
+ * @param focusedTabProvider
+ * provides the current focused tab.
+ */
+ public BlurCommand(Tab blurSource) {
+ this.blurSource = blurSource;
+ }
+
+ /**
+ * Stop the command from being executed.
+ *
+ * @since
+ */
+ public void stopSchedule() {
+ blurSource = null;
}
- if (focusedTab.hasTooltip()) {
- focusedTab.setAssistiveDescription(getVTooltip().getUniqueId());
- getVTooltip().showAssistive(focusedTab.getTooltipInfo());
+ /**
+ * Schedule the command for a deferred execution.
+ *
+ * @since
+ */
+ public void scheduleDeferred() {
+ Scheduler.get().scheduleDeferred(this);
+ }
+
+ @Override
+ public void execute() {
+
+ Tab focusedTab = getFocusedTab();
+
+ if (blurSource == null) {
+ return;
+ }
+
+ // The focus didn't change since this blur triggered, so
+ // the new focused element is not a tab.
+ if (focusedTab == blurSource) {
+
+ // We're certain there's no focus anymore.
+ focusedTab.removeAssistiveDescription();
+ setFocusedTab(null);
+
+ connector.getRpcProxy(FocusAndBlurServerRpc.class).blur();
+ }
+
+ // Call this to set it to null and be consistent.
+ cancelLastBlurSchedule();
}
}
+
+ /*
+ * Schedule a new blur event for a deferred execution.
+ */
+ private void scheduleBlur(Tab blurSource) {
+
+ if (nextBlurScheduleCancelled) {
+
+ // This will set the stopNextBlurCommand back to false as well.
+ cancelLastBlurSchedule();
+
+ // Reset the status.
+ nextBlurScheduleCancelled = false;
+ return;
+ }
+
+ cancelLastBlurSchedule();
+
+ blurCommand = new BlurCommand(blurSource);
+ blurCommand.scheduleDeferred();
+ }
+
+ /**
+ * Remove the last blur deferred command from execution.
+ */
+ public void cancelLastBlurSchedule() {
+ if (blurCommand != null) {
+ blurCommand.stopSchedule();
+ blurCommand = null;
+ }
+
+ // We really want to make sure this flag gets reseted at any time
+ // when something interact with the blur manager and ther's no blur
+ // command scheduled (as we just canceled it).
+ nextBlurScheduleCancelled = false;
+ }
+
+ /**
+ * Cancel the next scheduled execution. This method must be called only
+ * from an event occurring before the onBlur event. It's the case of IE
+ * which doesn't trigger the focus event, so we're using this approach
+ * to cancel the next blur event prior it's execution, calling the
+ * method from mouse down event.
+ */
+ public void cancelNextBlurSchedule() {
+
+ // Make sure there's still no other command to be executed.
+ cancelLastBlurSchedule();
+
+ nextBlurScheduleCancelled = true;
+ }
+
+ /*
+ * Flag that the next deferred command won't get executed. This is
+ * useful in case of IE where the user focus event don't fire and we're
+ * using the mouse down event to track the focus. But the mouse down
+ * event triggers before the blur, so we need to cancel the deferred
+ * execution in advance.
+ */
+ private boolean nextBlurScheduleCancelled = false;
+
}
@Override
- public void focus() {
- tb.getTab(activeTabIndex).focus();
+ public void onBlur(BlurEvent event) {
+ selectionHandler.onBlur(event);
}
- public void blur() {
- tb.getTab(activeTabIndex).blur();
+ @Override
+ public void onFocus(FocusEvent event) {
+ selectionHandler.onFocus(event);
}
@Override
public void onKeyDown(KeyDownEvent event) {
- if (event.getSource() instanceof Tab) {
- int keycode = event.getNativeEvent().getKeyCode();
-
- if (!event.isAnyModifierKeyDown()) {
- if (keycode == getPreviousTabKey()) {
- selectPreviousTab();
- event.stopPropagation();
- } else if (keycode == getNextTabKey()) {
- selectNextTab();
- event.stopPropagation();
- } else if (keycode == getCloseTabKey()) {
- Tab tab = tb.getTab(activeTabIndex);
- if (tab.isClosable()) {
- tab.onClose();
+ selectionHandler.onKeyDown(event);
+ }
+
+ /*
+ * The tabs selection handler instance.
+ */
+ private final TabSelectionHandler selectionHandler = new TabSelectionHandler();
+
+ /*
+ * Handle the events for selecting the tabs.
+ */
+ private class TabSelectionHandler implements FocusHandler, BlurHandler,
+ KeyDownHandler, ClickHandler, MouseDownHandler {
+
+ /** For internal use only. May be removed or replaced in the future. */
+ // The current visible focused index.
+ private int focusedTabIndex = 0;
+
+ /**
+ * Register the tab to the selection handler.
+ *
+ * @param tab
+ * the tab to register.
+ */
+ public void registerTab(Tab tab) {
+
+ // TODO: change VTabsheet.this to this in 7.3
+ tab.addBlurHandler(VTabsheet.this);
+ tab.addFocusHandler(VTabsheet.this);
+ tab.addKeyDownHandler(VTabsheet.this);
+
+ tab.addClickHandler(this);
+ tab.addMouseDownHandler(this);
+ }
+
+ @Override
+ public void onBlur(final BlurEvent event) {
+
+ getVTooltip().hideTooltip();
+
+ Object blurSource = event.getSource();
+
+ if (blurSource instanceof Tab) {
+ focusBlurManager.onBlur((Tab) blurSource);
+ }
+ }
+
+ @Override
+ public void onFocus(FocusEvent event) {
+
+ if (event.getSource() instanceof Tab) {
+ Tab focusSource = (Tab) event.getSource();
+ focusBlurManager.onFocus(focusSource);
+
+ if (focusSource.hasTooltip()) {
+ focusSource.setAssistiveDescription(getVTooltip()
+ .getUniqueId());
+ getVTooltip().showAssistive(focusSource.getTooltipInfo());
+ }
+
+ }
+ }
+
+ @Override
+ public void onClick(ClickEvent event) {
+
+ // IE doesn't trigger focus when click, so we need to make sure
+ // the previous blur deferred command will get killed.
+ focusBlurManager.cancelLastBlurSchedule();
+
+ TabCaption caption = (TabCaption) event.getSource();
+ Element targetElement = event.getNativeEvent().getEventTarget()
+ .cast();
+ // the tab should not be focused if the close button was clicked
+ if (targetElement == caption.getCloseButton()) {
+ return;
+ }
+
+ int index = tb.getWidgetIndex(caption.getParent());
+
+ tb.navigateTab(focusedTabIndex, index);
+
+ focusedTabIndex = index;
+
+ if (!loadTabSheet(index)) {
+
+ // This needs to be called at the end, as the activeTabIndex
+ // is set in the loadTabSheet.
+ focus();
+ }
+ }
+
+ @Override
+ public void onMouseDown(MouseDownEvent event) {
+
+ if (event.getSource() instanceof Tab) {
+
+ // IE doesn't trigger focus when click, so we need to make sure
+ // the
+ // next blur deferred command will get killed.
+ focusBlurManager.cancelNextBlurSchedule();
+ }
+ }
+
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ if (event.getSource() instanceof Tab) {
+ int keycode = event.getNativeEvent().getKeyCode();
+
+ if (!event.isAnyModifierKeyDown()) {
+ if (keycode == getPreviousTabKey()) {
+ selectPreviousTab();
+ event.stopPropagation();
+
+ } else if (keycode == getNextTabKey()) {
+ selectNextTab();
+ event.stopPropagation();
+
+ } else if (keycode == getCloseTabKey()) {
+ Tab tab = tb.getTab(activeTabIndex);
+ if (tab.isClosable()) {
+ tab.onClose();
+ }
+
+ } else if (keycode == getSelectTabKey()) {
+ loadTabSheet(focusedTabIndex);
+
+ // Prevent the page from scrolling when hitting space
+ // (select key) to select the current tab.
+ event.preventDefault();
}
- } else if (keycode == getSelectTabKey()) {
- loadTabSheet(focusedTabIndex);
}
}
}
+
+ /*
+ * Left arrow key selection.
+ */
+ private void selectPreviousTab() {
+ int newTabIndex = focusedTabIndex;
+ // Find the previous visible and enabled tab if any.
+ do {
+ newTabIndex--;
+ } while (newTabIndex >= 0 && !canSelectTab(newTabIndex));
+
+ if (newTabIndex >= 0) {
+ keySelectTab(newTabIndex);
+ }
+ }
+
+ /*
+ * Right arrow key selection.
+ */
+ private void selectNextTab() {
+ int newTabIndex = focusedTabIndex;
+ // Find the next visible and enabled tab if any.
+ do {
+ newTabIndex++;
+ } while (newTabIndex < getTabCount() && !canSelectTab(newTabIndex));
+
+ if (newTabIndex < getTabCount()) {
+ keySelectTab(newTabIndex);
+ }
+ }
+
+ /*
+ * Select the specified tab using left/right key.
+ */
+ private void keySelectTab(int newTabIndex) {
+ Tab tab = tb.getTab(newTabIndex);
+ if (tab == null) {
+ return;
+ }
+
+ // Focus the tab, otherwise the selected one will loose focus and
+ // TabSheet will get blurred.
+ focusTabAtIndex(newTabIndex);
+
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+
+ focusedTabIndex = newTabIndex;
+ }
+
+ /**
+ * Focus the specified tab. Make sure to call this only from user
+ * events, otherwise will break things.
+ *
+ * @param tabIndex
+ * the index of the tab to set.
+ */
+ void focusTabAtIndex(int tabIndex) {
+ Tab tabToFocus = tb.getTab(tabIndex);
+ if (tabToFocus != null) {
+ tabToFocus.focus();
+ }
+ }
+
}
/**
@@ -1307,8 +1751,17 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return KeyCodes.KEY_LEFT;
}
+ /**
+ * Gets the key to activate the selected tab when navigating using
+ * previous/next (left/right) keys.
+ *
+ * @return the key to activate the selected tab.
+ *
+ * @see #getNextTabKey()
+ * @see #getPreviousTabKey()
+ */
protected int getSelectTabKey() {
- return 32; // Space key
+ return KeyCodes.KEY_SPACE;
}
/**
@@ -1327,66 +1780,32 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return KeyCodes.KEY_DELETE;
}
- private void selectPreviousTab() {
- int newTabIndex = focusedTabIndex;
- // Find the previous visible and enabled tab if any.
- do {
- newTabIndex--;
- } while (newTabIndex >= 0 && !canSelectTab(newTabIndex));
-
- if (newTabIndex >= 0) {
- tb.navigateTab(focusedTabIndex, newTabIndex);
- focusedTabIndex = newTabIndex;
-
- // If this TabSheet already has focus, set the new selected tab
- // as focused.
- if (focusedTab != null) {
- focusedTab = tb.getTab(focusedTabIndex);
- focusedTab.focus();
- }
- }
- }
-
- private void selectNextTab() {
- int newTabIndex = focusedTabIndex;
- // Find the next visible and enabled tab if any.
- do {
- newTabIndex++;
- } while (newTabIndex < getTabCount() && !canSelectTab(newTabIndex));
-
- if (newTabIndex < getTabCount()) {
+ private void scrollIntoView(Tab tab) {
- tb.navigateTab(focusedTabIndex, newTabIndex);
- focusedTabIndex = newTabIndex;
+ if (!tab.isHiddenOnServer()) {
- // If this TabSheet already has focus, set the new selected tab
- // as focused.
- if (focusedTab != null) {
- focusedTab = tb.getTab(focusedTabIndex);
- focusedTab.focus();
- }
- }
- }
+ // Check for visibility first as clipped tabs to the right are
+ // always visible.
+ // On IE8 a tab with false visibility would have the bounds of the
+ // full TabBar.
+ if (!tab.isVisible()) {
+ while (!tab.isVisible()) {
+ scrollerIndex = tb.scrollLeft(scrollerIndex);
+ }
+ updateTabScroller();
- private void scrollIntoView(Tab tab) {
- if (!tab.isHiddenOnServer()) {
- if (isClipped(tab)) {
+ } else if (isClipped(tab)) {
while (isClipped(tab) && scrollerIndex != -1) {
scrollerIndex = tb.scrollRight(scrollerIndex);
}
updateTabScroller();
- } else if (!tab.isVisible()) {
- while (!tab.isVisible()) {
- scrollerIndex = tb.scrollLeft(scrollerIndex);
- }
- updateTabScroller();
}
}
}
/**
* Makes tab bar visible.
- *
+ *
* @since 7.2
*/
public void showTabs() {
@@ -1397,7 +1816,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* Makes tab bar invisible.
- *
+ *
* @since 7.2
*/
public void hideTabs() {
diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java
index 495e230156..7223e4ac83 100644
--- a/client/src/com/vaadin/client/ui/VWindow.java
+++ b/client/src/com/vaadin/client/ui/VWindow.java
@@ -245,6 +245,20 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
* state.
*/
setTabStopEnabled(doTabStop);
+
+ // Fix for #14413. Any pseudo elements inside these elements are not
+ // visible on initial render unless we shake the DOM.
+ if (BrowserInfo.get().isIE8()) {
+ closeBox.getStyle().setDisplay(Display.NONE);
+ maximizeRestoreBox.getStyle().setDisplay(Display.NONE);
+ Scheduler.get().scheduleFinally(new Command() {
+ @Override
+ public void execute() {
+ closeBox.getStyle().clearDisplay();
+ maximizeRestoreBox.getStyle().clearDisplay();
+ }
+ });
+ }
}
@Override
diff --git a/client/src/com/vaadin/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java b/client/src/com/vaadin/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
index afb521b141..f222721e24 100644
--- a/client/src/com/vaadin/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
+++ b/client/src/com/vaadin/client/ui/draganddropwrapper/DragAndDropWrapperConnector.java
@@ -17,8 +17,12 @@ package com.vaadin.client.ui.draganddropwrapper;
import java.util.HashMap;
import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.vaadin.client.VConsole;
@@ -81,6 +85,25 @@ public class DragAndDropWrapperConnector extends CustomComponentConnector
getWidget().dragStartMode = uidl
.getIntAttribute(DragAndDropWrapperConstants.DRAG_START_MODE);
+
+ String dragImageComponentConnectorId = uidl
+ .getStringAttribute(DragAndDropWrapperConstants.DRAG_START_COMPONENT_ATTRIBUTE);
+
+ ComponentConnector connector = null;
+ if (dragImageComponentConnectorId != null) {
+ connector = (ComponentConnector) ConnectorMap.get(client)
+ .getConnector(dragImageComponentConnectorId);
+
+ if (connector == null) {
+ getLogger().log(
+ Level.WARNING,
+ "DragAndDropWrapper drag image component"
+ + " connector now found. Make sure the"
+ + " component is attached.");
+ } else {
+ getWidget().setDragAndDropWidget(connector.getWidget());
+ }
+ }
getWidget().initDragStartMode();
getWidget().html5DataFlavors = uidl
.getMapAttribute(DragAndDropWrapperConstants.HTML5_DATA_FLAVORS);
@@ -95,4 +118,7 @@ public class DragAndDropWrapperConnector extends CustomComponentConnector
return (VDragAndDropWrapper) super.getWidget();
}
+ private static Logger getLogger() {
+ return Logger.getLogger(DragAndDropWrapperConnector.class.getName());
+ }
}
diff --git a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java
index 5b428574e2..e38054e3e4 100644
--- a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java
+++ b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java
@@ -28,6 +28,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase {
private static final String IE10_WINDOWS_8 = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
private static final String IE11_WINDOWS_7 = "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; rv:11.0) like Gecko";
+ private static final String IE11_WINDOWS_PHONE_8_1_UPDATE = "Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920) Like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537";
// "Version/" was added in 10.00
private static final String OPERA964_WINDOWS = "Opera/9.64(Windows NT 5.1; U; en) Presto/2.1.1";
@@ -396,6 +397,16 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase {
assertWindows(bd);
}
+ public void testIE11WindowsPhone81Update() {
+ VBrowserDetails bd = new VBrowserDetails(IE11_WINDOWS_PHONE_8_1_UPDATE);
+ assertTrident(bd);
+ assertEngineVersion(bd, 7);
+ assertIE(bd);
+ assertBrowserMajorVersion(bd, 11);
+ assertBrowserMinorVersion(bd, 0);
+ assertWindows(bd);
+ }
+
/*
* Helper methods below
*/