During row mouseout events, only consider it as row unhovering if the event coordinates are outside the hovering row. Previously, if there's a popup dialog floating on top of a table row, moving into this element will cause a row unhover event. This is particularly a problem if the element is shown/dismissed based on row hovering (causing a flickring effect). Also include the original browser event in the RowHoverEvent in case the user needs more information about the original event. Review at http://gwt-code-reviews.appspot.com/1546803 Review by: jlabanca@google.com git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10660 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/cellview/client/AbstractCellTable.java b/user/src/com/google/gwt/user/cellview/client/AbstractCellTable.java index b17b737..f51f92a 100644 --- a/user/src/com/google/gwt/user/cellview/client/AbstractCellTable.java +++ b/user/src/com/google/gwt/user/cellview/client/AbstractCellTable.java
@@ -44,6 +44,7 @@ import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant; import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant; import com.google.gwt.user.client.ui.Widget; @@ -1777,13 +1778,26 @@ if ("mouseover".equals(eventType)) { // Unstyle the old row if it is still part of the table. if (hoveringRow != null && getTableBodyElement().isOrHasChild(hoveringRow)) { - setRowHover(hoveringRow, false); + setRowHover(hoveringRow, event, false); } hoveringRow = targetTableRow; - setRowHover(hoveringRow, true); + setRowHover(hoveringRow, event, true); } else if ("mouseout".equals(eventType) && hoveringRow != null) { - setRowHover(hoveringRow, false); - hoveringRow = null; + // Ignore events happening directly over the hovering row. If there are floating element + // on top of the row, mouseout event should not be triggered. This is to avoid the flickring + // effect if the floating element is shown/hide based on hover event. + int clientX = event.getClientX() + Window.getScrollLeft(); + int clientY = event.getClientY() + Window.getScrollTop(); + int rowLeft = hoveringRow.getAbsoluteLeft(); + int rowTop = hoveringRow.getAbsoluteTop(); + int rowWidth = hoveringRow.getOffsetWidth(); + int rowHeight = hoveringRow.getOffsetHeight(); + int rowBottom = rowTop + rowHeight; + int rowRight = rowLeft + rowWidth; + if (clientX < rowLeft || clientX > rowRight || clientY < rowTop || clientY > rowBottom) { + setRowHover(hoveringRow, event, false); + hoveringRow = null; + } } // If the event causes us to page, then the physical index will be out @@ -2410,11 +2424,12 @@ * Set a row's hovering style and fire a {@link RowHoverEvent} * * @param tr the row element + * @param event the original event * @param isHovering false if this is an unhover event */ - private void setRowHover(TableRowElement tr, boolean isHovering) { + private void setRowHover(TableRowElement tr, Event event, boolean isHovering) { setRowStyleName(tr, style.hoveredRow(), style.hoveredRowCell(), isHovering); - RowHoverEvent.fire(this, tr, !isHovering); + RowHoverEvent.fire(this, tr, event, !isHovering); } /**
diff --git a/user/src/com/google/gwt/user/cellview/client/RowHoverEvent.java b/user/src/com/google/gwt/user/cellview/client/RowHoverEvent.java index 77a8af9..43d1ea8 100644 --- a/user/src/com/google/gwt/user/cellview/client/RowHoverEvent.java +++ b/user/src/com/google/gwt/user/cellview/client/RowHoverEvent.java
@@ -19,6 +19,7 @@ import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HasHandlers; +import com.google.gwt.user.client.Event; /** * Represents a row hover event. @@ -55,7 +56,23 @@ */ public static RowHoverEvent fire(HasHandlers source, TableRowElement hoveringRow, boolean isUnHover) { - RowHoverEvent event = new RowHoverEvent(hoveringRow, isUnHover); + return fire(source, hoveringRow, null, isUnHover); + } + + /** + * Fires a row hover event on all registered handlers in the handler + * manager. If no such handlers exist, this implementation will do nothing. + * + * @param source the source of the event + * @param hoveringRow the currently hovering {@link TableRowElement}. If isUnHover is false, this + * should be the previouly hovering {@link TableRowElement} + * @param browserEvent the original browser event + * @param isUnHover false if this is an unhover event + * @return the {@link RowHoverEvent} that was fired + */ + public static RowHoverEvent fire(HasHandlers source, TableRowElement hoveringRow, + Event browserEvent, boolean isUnHover) { + RowHoverEvent event = new RowHoverEvent(hoveringRow, browserEvent, isUnHover); if (TYPE != null) { source.fireEvent(event); } @@ -73,6 +90,8 @@ } return TYPE; } + + private Event browserEvent; private TableRowElement hoveringRow; @@ -86,7 +105,20 @@ * @param isUnHover false if this is an unhover event */ protected RowHoverEvent(TableRowElement hoveringRow, boolean isUnHover) { + this(hoveringRow, null, isUnHover); + } + + /** + * Construct a new {@link RowHoverEvent}. + * + * @param hoveringRow the currently hovering {@link TableRowElement}. If isUnHover is false, this + * should be the previouly hovering {@link TableRowElement} + * @param browserEvent the original browser event + * @param isUnHover false if this is an unhover event + */ + protected RowHoverEvent(TableRowElement hoveringRow, Event browserEvent, boolean isUnHover) { this.hoveringRow = hoveringRow; + this.browserEvent = browserEvent; this.isUnHover = isUnHover; } @@ -96,6 +128,14 @@ } /** + * Return the original browser {@link Event}. The browser event could be null if the event is + * fired without one (e.g., by calling {@link #fire(HasHandler, TableRowElement, isUnHover)}) + */ + public Event getBrowserEvent() { + return browserEvent; + } + + /** * Return the {@link TableRowElement} that the user just hovered or unhovered. */ public TableRowElement getHoveringRow() {