import java.util.List;
import java.util.Set;
+import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
private int popupOuterPadding = -1;
private int topPosition;
+ private int leftPosition;
private final MouseWheeler mouseWheeler = new MouseWheeler();
+ private boolean scrollPending = false;
+
/**
* Default constructor
*/
// Add TT anchor point
getElement().setId("VAADIN_COMBOBOX_OPTIONLIST");
- final int x = toInt32(WidgetUtil
- .getBoundingClientRect(VComboBox.this.getElement())
- .getLeft());
-
- topPosition = toInt32(WidgetUtil
- .getBoundingClientRect(tb.getElement()).getBottom())
- + Window.getScrollTop();
+ leftPosition = getDesiredLeftPosition();
+ topPosition = getDesiredTopPosition();
- setPopupPosition(x, topPosition);
+ setPopupPosition(leftPosition, topPosition);
int nullOffset = getNullSelectionItemShouldBeVisible() ? 1 : 0;
boolean firstPage = currentPage == 0;
setPopupPositionAndShow(popup);
}
+ private int getDesiredTopPosition() {
+ return toInt32(WidgetUtil.getBoundingClientRect(tb.getElement())
+ .getBottom()) + Window.getScrollTop();
+ }
+
+ private int getDesiredLeftPosition() {
+ return toInt32(WidgetUtil
+ .getBoundingClientRect(VComboBox.this.getElement())
+ .getLeft());
+ }
+
private native int toInt32(double val)
/*-{
return val | 0;
handleMouseDownEvent(event);
}
+ @Override
+ protected void onPreviewNativeEvent(NativePreviewEvent event) {
+ // Check all events outside the combobox to see if they scroll the
+ // page. We cannot use e.g. Window.addScrollListener() because the
+ // scrolled element can be at any level on the page.
+
+ // Normally this is only called when the popup is showing, but make
+ // sure we don't accidentally process all events when not showing.
+ if (!scrollPending && isShowing() && !DOM.isOrHasChild(
+ SuggestionPopup.this.getElement(),
+ Element.as(event.getNativeEvent().getEventTarget()))) {
+ if (getDesiredLeftPosition() != leftPosition
+ || getDesiredTopPosition() != topPosition) {
+ updatePopupPositionOnScroll();
+ }
+ }
+
+ super.onPreviewNativeEvent(event);
+ }
+
+ /**
+ * Make the popup follow the position of the ComboBox when the page is
+ * scrolled.
+ */
+ private void updatePopupPositionOnScroll() {
+ if (!scrollPending) {
+ AnimationScheduler.get().requestAnimationFrame(timestamp -> {
+ if (isShowing()) {
+ leftPosition = getDesiredLeftPosition();
+ topPosition = getDesiredTopPosition();
+ setPopupPosition(leftPosition, topPosition);
+ }
+ scrollPending = false;
+ });
+ scrollPending = true;
+ }
+ }
+
/**
* Should paging be enabled. If paging is enabled then only a certain
* amount of items are visible at a time and a scrollbar or buttons are
/**
* Gets the empty selection caption.
- *
+ *
* @since 8.0.7
* @return the empty selection caption
*/
*
* @param emptySelectionCaption
* the empty selection caption
- *
+ *
* @since 8.0.7
*/
public void setEmptySelectionCaption(String emptySelectionCaption) {
import java.util.ArrayList;
import java.util.List;
+import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+@Widgetset("com.vaadin.DefaultWidgetSet")
public class ComboboxPopupScrolling extends AbstractTestUIWithLog {
@Override
protected void setup(VaadinRequest request) {
- ComboBox combobox = new ComboBox("100px wide combobox");
+ ComboBox<String> combobox = new ComboBox<>("100px wide combobox");
combobox.setWidth("100px");
combobox.setItems("AMERICAN SAMOA", "ANTIGUA AND BARBUDA");
- ComboBox combobox2 = new ComboBox("250px wide combobox");
+ ComboBox<String> combobox2 = new ComboBox<>("250px wide combobox");
combobox2.setWidth("250px");
combobox2.setItems("AMERICAN SAMOA", "ANTIGUA AND BARBUDA");
- ComboBox combobox3 = new ComboBox("Undefined wide combobox");
+ ComboBox<String> combobox3 = new ComboBox<>("Undefined wide combobox");
combobox3.setWidth(null);
combobox3.setItems("AMERICAN SAMOA", "ANTIGUA AND BARBUDA");
- ComboBox combobox4 = new ComboBox("Another 100px wide combobox");
+ ComboBox<String> combobox4 = new ComboBox<>(
+ "Another 100px wide combobox");
combobox4.setWidth("100px");
List<String> items = new ArrayList<>();
for (int i = 0; i < 10; i++) {
HorizontalLayout hl = new HorizontalLayout(combobox, combobox2,
combobox3, combobox4);
addComponent(hl);
+
+ Label spacer = new Label();
+ spacer.setHeight("800px");
+ addComponent(spacer);
}
}
\ No newline at end of file
*/
package com.vaadin.tests.components.combobox;
+import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
+import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;
import com.vaadin.testbench.elements.ComboBoxElement;
+import com.vaadin.testbench.elements.UIElement;
import com.vaadin.tests.tb3.MultiBrowserTest;
public class ComboboxPopupScrollingTest extends MultiBrowserTest {
testNoScrollbars("reindeer");
}
+ @Test
+ public void testComboBoxTracksScrolledPage() {
+ openTestURL("theme=valo");
+
+ ComboBoxElement cb = $(ComboBoxElement.class).last();
+ cb.openPopup();
+ WebElement popup = cb.getSuggestionPopup();
+ Point comboLocation = cb.getLocation();
+ Point popupLocation = popup.getLocation();
+
+ // scroll page
+ $(UIElement.class).first().scroll(100);
+
+ // make sure animation frame is handled
+ sleep(500);
+
+ Point newComboLocation = cb.getLocation();
+ Point newPopupLocation = popup.getLocation();
+ Assert.assertNotEquals("ComboBox didn't move on the page", 0,
+ newComboLocation.y - comboLocation.y);
+ Assert.assertEquals("Popup didn't move with the combo box",
+ newComboLocation.y - comboLocation.y,
+ newPopupLocation.y - popupLocation.y);
+ }
+
private void testNoScrollbars(String theme) {
openTestURL("theme=" + theme);