From 30ba9123951b2362e5e1efadcca169f8683de5d9 Mon Sep 17 00:00:00 2001
From: Henri Sara
Date: Tue, 15 Nov 2011 14:52:47 +0000
Subject: Merged #7837 to Vaadin 6.8
svn changeset:22010/svn branch:6.8
---
src/com/vaadin/ui/TreeTable.java | 20 ++-
.../FileSystemContainerInTreeTable.html | 117 +++++++++++++++++
.../FileSystemContainerInTreeTable.java | 141 +++++++++++++++++++++
3 files changed, 276 insertions(+), 2 deletions(-)
create mode 100644 tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.html
create mode 100644 tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.java
diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java
index 9cba7362ac..fb12ffe323 100644
--- a/src/com/vaadin/ui/TreeTable.java
+++ b/src/com/vaadin/ui/TreeTable.java
@@ -333,6 +333,12 @@ public class TreeTable extends Table implements Hierarchical {
private boolean animationsEnabled;
private boolean clearFocusedRowPending;
+ /**
+ * If the container does not send item set change events, always do a full
+ * repaint instead of a partial update when expanding/collapsing nodes.
+ */
+ private boolean containerSupportsPartialUpdates;
+
private ContainerStrategy getContainerStrategy() {
if (cStrategy == null) {
if (getContainerDataSource() instanceof Collapsible) {
@@ -464,7 +470,7 @@ public class TreeTable extends Table implements Hierarchical {
@Override
protected boolean isPartialRowUpdate() {
- return toggledItemId != null;
+ return toggledItemId != null && containerSupportsPartialUpdates;
}
@Override
@@ -515,13 +521,20 @@ public class TreeTable extends Table implements Hierarchical {
// ensure that page still has first item in page, DON'T clear the
// caches.
setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
- requestRepaint();
if (isCollapsed(itemId)) {
fireCollapseEvent(itemId);
} else {
fireExpandEvent(itemId);
}
+
+ if (containerSupportsPartialUpdates) {
+ requestRepaint();
+ } else {
+ // For containers that do not send item set change events, always do
+ // full repaint instead of partial row update.
+ refreshRowCache();
+ }
}
@Override
@@ -537,6 +550,9 @@ public class TreeTable extends Table implements Hierarchical {
@Override
public void setContainerDataSource(Container newDataSource) {
cStrategy = null;
+
+ containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier);
+
if (!(newDataSource instanceof Hierarchical)) {
newDataSource = new ContainerHierarchicalWrapper(newDataSource);
}
diff --git a/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.html b/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.html
new file mode 100644
index 0000000000..b5ccd53df0
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+FileSystemContainerInTreeTable
+
+
+
+
+FileSystemContainerInTreeTable
+
+
+ open
+ /run/com.vaadin.tests.containers.filesystemcontainer.FileSystemContainerInTreeTable?restartApplication
+
+
+
+ screenCapture
+
+ initial
+
+
+ click
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]
+
+
+
+ screenCapture
+
+ added_not_yet_visible
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]
+ 30,8
+
+
+ screenCapture
+
+ added_dir2_expanded
+
+
+ click
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]/domChild[0]
+ 32,6
+
+
+ screenCapture
+
+ deleted_dir2_collapsed
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]
+ 32,4
+
+
+ screenCapture
+
+ deleted_dir2_expanded
+
+
+ click
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]
+ 31,8
+
+
+ screenCapture
+
+ added_dir2_collapsed
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]
+ 29,5
+
+
+ screenCapture
+
+ added_dir1_expanded
+
+
+ click
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTreeTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]
+ 32,8
+
+
+ screenCapture
+
+ deleted_dir1_collapsed
+
+
+ click
+ vaadin=runcomvaadintestscontainersfilesystemcontainerFileSystemContainerInTreeTable::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]
+
+
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.java b/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.java
new file mode 100644
index 0000000000..b3b5185505
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/containers/filesystemcontainer/FileSystemContainerInTreeTable.java
@@ -0,0 +1,141 @@
+package com.vaadin.tests.containers.filesystemcontainer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Container.Ordered;
+import com.vaadin.data.util.FilesystemContainer;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Tree.CollapseEvent;
+import com.vaadin.ui.Tree.CollapseListener;
+import com.vaadin.ui.Tree.ExpandEvent;
+import com.vaadin.ui.Tree.ExpandListener;
+import com.vaadin.ui.TreeTable;
+
+public class FileSystemContainerInTreeTable extends TestBase {
+
+ private Log log = new Log(5);
+ private TreeTable treeTable;
+
+ @Override
+ protected void setup() {
+ setTheme("reindeer-tests");
+
+ final File folder;
+ try {
+ File tempFile = File.createTempFile("fsc-tt", "");
+ tempFile.delete();
+ folder = new File(tempFile.getParent(), tempFile.getName());
+ folder.mkdir();
+ System.out.println(folder.getPath());
+ folder.deleteOnExit();
+
+ populate(folder, 3, 10);
+
+ FilesystemContainer fsc = new FilesystemContainer(folder);
+
+ treeTable = new TreeTable();
+ treeTable.addStyleName("table-equal-rowheight");
+ treeTable.setWidth("450px");
+ treeTable.setHeight("550px");
+ treeTable.setContainerDataSource(fsc);
+ treeTable.setItemIconPropertyId(FilesystemContainer.PROPERTY_ICON);
+ treeTable.setVisibleColumns(new String[] { "Name" });
+ treeTable.setColumnWidth("Name", 400);
+ treeTable.addListener(new ExpandListener() {
+
+ public void nodeExpand(ExpandEvent event) {
+ logExpandCollapse(event.getItemId(), "expanded");
+
+ }
+ });
+ treeTable.addListener(new CollapseListener() {
+
+ public void nodeCollapse(CollapseEvent event) {
+ logExpandCollapse(event.getItemId(), "collapsed");
+
+ }
+ });
+
+ addComponent(log);
+ addComponent(treeTable);
+
+ HorizontalLayout buttonLayout = new HorizontalLayout();
+ buttonLayout.setSpacing(true);
+ buttonLayout.addComponent(new Button("Create dir11",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ new File(folder, "dir11").mkdir();
+ log.log("Row dir11 created");
+ }
+ }));
+ buttonLayout.addComponent(new Button("Delete dir11",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ new File(folder, "dir11").delete();
+ log.log("Row dir11 deleted");
+ }
+ }));
+ // to clean up explicitly before ending an automated test
+ buttonLayout.addComponent(new Button("Clean all files",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ folder.delete();
+ }
+ }));
+ addComponent(buttonLayout);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void populate(File folder, int subDirectories, int files)
+ throws IOException {
+ for (int i = 1; i <= files; i++) {
+ File f = new File(folder, "file" + i + ".txt");
+ f.createNewFile();
+ }
+
+ for (int i = 1; i <= subDirectories; i++) {
+ File f = new File(folder, "dir" + i);
+ f.mkdir();
+ populate(f, 0, 2);
+ }
+ }
+
+ protected int indexOfId(Table source, Object itemId) {
+ Container.Ordered c = (Ordered) source.getContainerDataSource();
+ if (c instanceof Container.Indexed) {
+ return ((Container.Indexed) source).indexOfId(itemId);
+ } else {
+ ArrayList list = new ArrayList(source.getItemIds());
+ return list.indexOf(itemId);
+ }
+ }
+
+ protected void logExpandCollapse(Object itemId, String operation) {
+ File file = (File) itemId;
+ // do not use the variable part (path) of file name
+ log.log("Row " + file.getName() + " " + operation + ". Row index: "
+ + indexOfId(treeTable, itemId));
+
+ }
+
+ @Override
+ protected String getDescription() {
+ return "TreeTable partial updates can only be used with a container that notifies the TreeTable of item set changes";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 7837;
+ }
+
+}
\ No newline at end of file
--
cgit v1.2.3
From d8e680ed8f80d912c172171e4d2d42ac5531fe28 Mon Sep 17 00:00:00 2001
From: Henri Sara
Date: Wed, 16 Nov 2011 10:39:05 +0000
Subject: Merged some changes from 6.7 to 6.8
svn changeset:22016/svn branch:6.8
---
WebContent/statictestfiles/EmbedSizeHostPage.html | 43 +++++++++++++
src/com/vaadin/terminal/gwt/client/ui/VView.java | 70 +++++++++++++++++-----
tests/integration_tests.xml | 16 ++---
.../vaadin/tests/integration/EmbedSizeTest.java | 55 +++++++++++++++++
4 files changed, 163 insertions(+), 21 deletions(-)
create mode 100644 WebContent/statictestfiles/EmbedSizeHostPage.html
create mode 100644 tests/testbench/com/vaadin/tests/integration/EmbedSizeTest.java
diff --git a/WebContent/statictestfiles/EmbedSizeHostPage.html b/WebContent/statictestfiles/EmbedSizeHostPage.html
new file mode 100644
index 0000000000..06dfd5dfd7
--- /dev/null
+++ b/WebContent/statictestfiles/EmbedSizeHostPage.html
@@ -0,0 +1,43 @@
+
+
+
+
+com.vaadin.tests.integration.EmbedSizeTest
+
+
+
+
+
+
+
Test page for resize events with embedded applications
+
+
You have to enable javascript in your browser to use an application built with Vaadin.
+
+
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java
index af87610944..cd9f0c903c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VView.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VView.java
@@ -14,6 +14,7 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.DomEvent.Type;
@@ -23,7 +24,6 @@ import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
@@ -109,7 +109,8 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
new ScheduledCommand() {
public void execute() {
- windowSizeMaybeChanged(getOffsetWidth(), getOffsetHeight());
+ windowSizeMaybeChanged(Window.getClientWidth(),
+ Window.getClientHeight());
}
});
@@ -576,25 +577,58 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
return getElement().getOffsetWidth() - getExcessWidth();
}
- // If not running standalone, we might be inside elements that don't
- // shrink with the browser window if our own components have
+ // If not running standalone, there might be multiple Vaadin apps
+ // that won't shrink with the browser window as the components have
// calculated widths (#3125)
- Element layoutElement = ((Widget) layout).getElement();
- Style layoutStyle = layoutElement.getStyle();
- // Set display:none to the entire application to get a width not
- // influenced by the contents
- String originalDisplay = layoutStyle.getDisplay();
- layoutStyle.setDisplay(Display.NONE);
+ // Find all Vaadin applications on the page
+ ArrayList vaadinApps = new ArrayList();
+ loadAppIdListFromDOM(vaadinApps);
+
+ // Store original styles here so they can be restored
+ ArrayList originalDisplays = new ArrayList(
+ vaadinApps.size());
+
+ String ownAppId = connection.getConfiguration().getRootPanelId();
+
+ // Set display: none for all Vaadin apps
+ for (int i = 0; i < vaadinApps.size(); i++) {
+ String appId = vaadinApps.get(i);
+ Element targetElement;
+ if (appId.equals(ownAppId)) {
+ // Only hide the contents of current application
+ targetElement = ((Widget) layout).getElement();
+ } else {
+ // Hide everything for other applications
+ targetElement = Document.get().getElementById(appId);
+ }
+ Style layoutStyle = targetElement.getStyle();
+
+ originalDisplays.add(i, layoutStyle.getDisplay());
+ layoutStyle.setDisplay(Display.NONE);
+ }
int w = getElement().getOffsetWidth() - getExcessWidth();
// Then restore the old display style before returning
- if (originalDisplay.length() == 0) {
- layoutStyle.clearDisplay();
- } else {
- layoutStyle.setDisplay(Display.valueOf(originalDisplay));
+ for (int i = 0; i < vaadinApps.size(); i++) {
+ String appId = vaadinApps.get(i);
+ Element targetElement;
+ if (appId.equals(ownAppId)) {
+ targetElement = ((Widget) layout).getElement();
+ } else {
+ targetElement = Document.get().getElementById(appId);
+ }
+ Style layoutStyle = targetElement.getStyle();
+ String originalDisplay = originalDisplays.get(i);
+
+ if (originalDisplay.length() == 0) {
+ layoutStyle.clearDisplay();
+ } else {
+ layoutStyle.setProperty("display", originalDisplay);
+ }
}
+
return w;
}
@@ -611,6 +645,14 @@ public class VView extends SimplePanel implements Container, ResizeHandler,
}
};
+ private native static void loadAppIdListFromDOM(ArrayList list)
+ /*-{
+ var j;
+ for(j in $wnd.vaadin.vaadinConfigurations) {
+ list.@java.util.Collection::add(Ljava/lang/Object;)(j);
+ }
+ }-*/;
+
public RenderSpace getAllocatedSpace(Widget child) {
return myRenderSpace;
}
diff --git a/tests/integration_tests.xml b/tests/integration_tests.xml
index 834c5fe608..46b6653a23 100644
--- a/tests/integration_tests.xml
+++ b/tests/integration_tests.xml
@@ -9,7 +9,7 @@
-
+
@@ -231,6 +231,7 @@
+
@@ -239,6 +240,7 @@
+
@@ -352,6 +354,7 @@
+
@@ -394,18 +397,17 @@
-
-
-
-
-
+
-
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/integration/EmbedSizeTest.java b/tests/testbench/com/vaadin/tests/integration/EmbedSizeTest.java
new file mode 100644
index 0000000000..479af4aa87
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/integration/EmbedSizeTest.java
@@ -0,0 +1,55 @@
+package com.vaadin.tests.integration;
+
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.Window.ResizeEvent;
+
+public class EmbedSizeTest extends TestBase {
+
+ private Log log = new Log(10);
+
+ @Override
+ protected void setup() {
+ Window mainWindow = getMainWindow();
+ mainWindow.setSizeUndefined();
+ mainWindow.getContent().setSizeUndefined();
+ mainWindow.setImmediate(true);
+
+ CheckBox lazyCheckBox = new CheckBox("Lazy resize",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ boolean resizeLazy = Boolean.TRUE == event.getButton()
+ .getValue();
+ getMainWindow().setResizeLazy(resizeLazy);
+ log.log("Resize lazy: " + resizeLazy);
+ }
+ });
+ lazyCheckBox.setValue(Boolean.FALSE);
+ lazyCheckBox.setImmediate(true);
+ addComponent(lazyCheckBox);
+
+ addComponent(log);
+ mainWindow.addListener(new Window.ResizeListener() {
+ public void windowResized(ResizeEvent e) {
+ Window window = e.getWindow();
+ log.log("Resize event: " + window.getWidth() + " x "
+ + window.getHeight());
+ }
+ });
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Resizing the browser window should send consistent resize events to the server even when the application is embedded";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(7923);
+ }
+
+}
--
cgit v1.2.3
From e8f99a58c30d2813f0ddc60787fe7dc8774dac4e Mon Sep 17 00:00:00 2001
From: Artur Signell
Date: Thu, 17 Nov 2011 13:37:04 +0000
Subject: Merged fixes from 6.7
svn changeset:22048/svn branch:6.8
---
WebContent/release-notes.html | 4 +-
.../terminal/gwt/client/ui/VFilterSelect.java | 277 +++++++++++++++------
src/com/vaadin/ui/Select.java | 2 +-
.../components/combobox/ComboBoxFiltering.html | 62 +++++
.../combobox/ComboBoxIdenticalItems.html | 112 +++++++++
.../combobox/ComboBoxIdenticalItems.java | 54 ++++
.../tests/components/combobox/ComboBoxInPopup.htm | 48 ++++
.../tests/components/combobox/ComboBoxInPopup.java | 50 ++++
.../components/combobox/ComboBoxNullItem.html | 62 +++++
.../tests/components/combobox/ComboBoxSlow.java | 55 ++++
.../tests/components/combobox/Comboboxes.html | 29 ++-
.../components/datefield/DateFieldTimezone.html | 3 +-
12 files changed, 664 insertions(+), 94 deletions(-)
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxFiltering.html
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.html
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.java
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.htm
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.java
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxNullItem.html
create mode 100644 tests/testbench/com/vaadin/tests/components/combobox/ComboBoxSlow.java
diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html
index b05a5e1548..574c3792ef 100644
--- a/WebContent/release-notes.html
+++ b/WebContent/release-notes.html
@@ -481,11 +481,11 @@
- Mozilla Firefox 3-6
+ Mozilla Firefox 3-8
Internet Explorer 6-9
Safari 4-5
Opera 10-11
- Google Chrome 13
+ Google Chrome 13-15
Vaadin on the Web
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
index 7c3d3066ce..66200cbe8c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java
@@ -7,8 +7,10 @@ package com.vaadin.terminal.gwt.client.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -49,6 +51,7 @@ import com.vaadin.terminal.gwt.client.Focusable;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.VTooltip;
/**
@@ -56,6 +59,7 @@ import com.vaadin.terminal.gwt.client.VTooltip;
*
* TODO needs major refactoring (to be extensible etc)
*/
+@SuppressWarnings("deprecation")
public class VFilterSelect extends Composite implements Paintable, Field,
KeyDownHandler, KeyUpHandler, ClickHandler, FocusHandler, BlurHandler,
Focusable {
@@ -96,7 +100,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
sb.append(Util.escapeAttribute(iconUri));
sb.append("\" alt=\"\" class=\"v-icon\" />");
}
- sb.append("" + Util.escapeHTML(caption) + " ");
+ String content;
+ if ("".equals(caption)) {
+ // Ensure that empty options use the same height as other
+ // options and are not collapsed (#7506)
+ content = " ";
+ } else {
+ content = Util.escapeHTML(caption);
+ }
+ sb.append("" + content + " ");
return sb.toString();
}
@@ -286,7 +298,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
.getText().length() - lastFilter.length());
} else if (hasNextPage()) {
- lastIndex = index - 1; // save for paging
+ selectPopupItemWhenResponseIsReceived = Select.FIRST;
filterOptions(currentPage + 1, lastFilter);
}
}
@@ -305,7 +317,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
.getText().length() - lastFilter.length());
} else if (index == -1) {
if (currentPage > 0) {
- lastIndex = index + 1; // save for paging
+ selectPopupItemWhenResponseIsReceived = Select.LAST;
filterOptions(currentPage - 1, lastFilter);
}
} else {
@@ -330,7 +342,20 @@ public class VFilterSelect extends Composite implements Paintable, Field,
@Override
public void run() {
if (pagesToScroll != 0) {
- filterOptions(currentPage + pagesToScroll, lastFilter);
+ if (!waitingForFilteringResponse) {
+ /*
+ * Avoid scrolling while we are waiting for a response
+ * because otherwise the waiting flag will be reset in
+ * the first response and the second response will be
+ * ignored, causing an empty popup...
+ *
+ * As long as the scrolling delay is suitable
+ * double/triple clicks will work by scrolling two or
+ * three pages at a time and this should not be a
+ * problem.
+ */
+ filterOptions(currentPage + pagesToScroll, lastFilter);
+ }
pagesToScroll = 0;
}
}
@@ -339,7 +364,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
if (currentPage + pagesToScroll > 0) {
pagesToScroll--;
cancel();
- schedule(100);
+ schedule(200);
}
}
@@ -348,7 +373,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* pageLength) {
pagesToScroll++;
cancel();
- schedule(100);
+ schedule(200);
}
}
}
@@ -538,6 +563,13 @@ public class VFilterSelect extends Composite implements Paintable, Field,
public class SuggestionMenu extends MenuBar implements SubPartAware,
LoadHandler {
+ /**
+ * Tracks the item that is currently selected using the keyboard. This
+ * is need only because mouseover changes the selection and we do not
+ * want to use that selection when pressing enter to select the item.
+ */
+ private MenuItem keyboardSelectedItem;
+
private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(
100, new ScheduledCommand() {
@@ -584,6 +616,10 @@ public class VFilterSelect extends Composite implements Paintable, Field,
*/
public void setSuggestions(
Collection suggestions) {
+ // Reset keyboard selection when contents is updated to avoid
+ // reusing old, invalid data
+ setKeyboardSelectedItem(null);
+
clearItems();
final Iterator it = suggestions.iterator();
while (it.hasNext()) {
@@ -623,8 +659,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
return;
}
- selecting = filtering;
- if (!filtering) {
+ updateSelectionWhenReponseIsReceived = waitingForFilteringResponse;
+ if (!waitingForFilteringResponse) {
doPostFilterSelectedItemAction();
}
}
@@ -636,7 +672,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
final MenuItem item = getSelectedItem();
final String enteredItemValue = tb.getText();
- selecting = false;
+ updateSelectionWhenReponseIsReceived = false;
// check for exact match in menu
int p = getItems().size();
@@ -737,6 +773,25 @@ public class VFilterSelect extends Composite implements Paintable, Field,
delayedImageLoadExecutioner.trigger();
}
+
+ public void selectFirstItem() {
+ MenuItem firstItem = getItems().get(0);
+ selectItem(firstItem);
+ }
+
+ private MenuItem getKeyboardSelectedItem() {
+ return keyboardSelectedItem;
+ }
+
+ private void setKeyboardSelectedItem(MenuItem firstItem) {
+ keyboardSelectedItem = firstItem;
+ }
+
+ public void selectLastItem() {
+ List items = getItems();
+ MenuItem lastItem = items.get(items.size() - 1);
+ selectItem(lastItem);
+ }
}
public static final int FILTERINGMODE_OFF = 0;
@@ -748,6 +803,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
protected int pageLength = 10;
+ private boolean enableDebug = false;
+
private final FlowPanel panel = new FlowPanel();
/**
@@ -821,19 +878,24 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* A collection of available suggestions (options) as received from the
* server.
*/
- private final Collection currentSuggestions = new ArrayList();
+ private final List currentSuggestions = new ArrayList();
private boolean immediate;
private String selectedOptionKey;
- private boolean filtering = false;
- private boolean selecting = false;
- private boolean tabPressed = false;
+ private boolean waitingForFilteringResponse = false;
+ private boolean updateSelectionWhenReponseIsReceived = false;
+ private boolean tabPressedWhenPopupOpen = false;
private boolean initDone = false;
private String lastFilter = "";
- private int lastIndex = -1; // last selected index when using arrows
+
+ private enum Select {
+ NONE, FIRST, LAST
+ };
+
+ private Select selectPopupItemWhenResponseIsReceived = Select.NONE;
/**
* The current suggestion selected from the dropdown. This is one of the
@@ -968,7 +1030,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
}
}
- filtering = true;
+ waitingForFilteringResponse = true;
client.updateVariable(paintableId, "filter", filter, false);
client.updateVariable(paintableId, "page", page, true);
lastFilter = filter;
@@ -1032,14 +1094,13 @@ public class VFilterSelect extends Composite implements Paintable, Field,
inputPrompt = "";
}
- suggestionPopup.setPagingEnabled(true);
suggestionPopup.updateStyleNames(uidl);
allowNewItem = uidl.hasAttribute("allownewitem");
lastNewItemString = null;
currentSuggestions.clear();
- if (!filtering) {
+ if (!waitingForFilteringResponse) {
/*
* Clear the current suggestions as the server response always
* includes the new ones. Exception is when filtering, then we need
@@ -1059,6 +1120,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
final UIDL options = uidl.getChildUIDL(0);
if (uidl.hasAttribute("totalMatches")) {
totalMatches = uidl.getIntAttribute("totalMatches");
+ } else {
+ totalMatches = 0;
}
// used only to calculate minimum popup width
@@ -1070,7 +1133,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
optionUidl);
currentSuggestions.add(suggestion);
if (optionUidl.hasAttribute("selected")) {
- if (!filtering || popupOpenerClicked) {
+ if (!waitingForFilteringResponse || popupOpenerClicked) {
String newSelectedOptionKey = Integer.toString(suggestion
.getOptionKey());
if (!newSelectedOptionKey.equals(selectedOptionKey)
@@ -1094,10 +1157,11 @@ public class VFilterSelect extends Composite implements Paintable, Field,
captions += Util.escapeHTML(suggestion.getReplacementString());
}
- if ((!filtering || popupOpenerClicked) && uidl.hasVariable("selected")
+ if ((!waitingForFilteringResponse || popupOpenerClicked)
+ && uidl.hasVariable("selected")
&& uidl.getStringArrayVariable("selected").length == 0) {
// select nulled
- if (!filtering || !popupOpenerClicked) {
+ if (!waitingForFilteringResponse || !popupOpenerClicked) {
if (!focused) {
/*
* client.updateComponent overwrites all styles so we must
@@ -1115,41 +1179,35 @@ public class VFilterSelect extends Composite implements Paintable, Field,
selectedOptionKey = null;
}
- if (filtering
+ if (waitingForFilteringResponse
&& lastFilter.toLowerCase().equals(
uidl.getStringVariable("filter"))) {
suggestionPopup.showSuggestions(currentSuggestions, currentPage,
totalMatches);
- filtering = false;
- if (!popupOpenerClicked && lastIndex != -1) {
+ waitingForFilteringResponse = false;
+ if (!popupOpenerClicked
+ && selectPopupItemWhenResponseIsReceived != Select.NONE) {
// we're paging w/ arrows
- MenuItem activeMenuItem;
- if (lastIndex == 0) {
- // going up, select last item
- int lastItem = pageLength - 1;
- List items = suggestionPopup.menu.getItems();
- /*
- * The first page can contain less than 10 items if the null
- * selection item is filtered away
- */
- if (lastItem >= items.size()) {
- lastItem = items.size() - 1;
- }
- activeMenuItem = items.get(lastItem);
- suggestionPopup.menu.selectItem(activeMenuItem);
+ if (selectPopupItemWhenResponseIsReceived == Select.LAST) {
+ suggestionPopup.menu.selectLastItem();
} else {
- // going down, select first item
- activeMenuItem = suggestionPopup.menu.getItems().get(0);
- suggestionPopup.menu.selectItem(activeMenuItem);
+ suggestionPopup.menu.selectFirstItem();
}
+ // This is used for paging so we update the keyboard selection
+ // variable as well.
+ MenuItem activeMenuItem = suggestionPopup.menu
+ .getSelectedItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(activeMenuItem);
+
+ // Update text field to contain the correct text
setTextboxText(activeMenuItem.getText());
tb.setSelectionRange(lastFilter.length(), activeMenuItem
.getText().length() - lastFilter.length());
- lastIndex = -1; // reset
+ selectPopupItemWhenResponseIsReceived = Select.NONE; // reset
}
- if (selecting) {
+ if (updateSelectionWhenReponseIsReceived) {
suggestionPopup.menu.doPostFilterSelectedItemAction();
}
}
@@ -1261,7 +1319,7 @@ public class VFilterSelect extends Composite implements Paintable, Field,
* The suggestion that just got selected.
*/
public void onSuggestionSelected(FilterSelectSuggestion suggestion) {
- selecting = false;
+ updateSelectionWhenReponseIsReceived = false;
currentSuggestion = suggestion;
String newKey;
@@ -1329,6 +1387,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
marginTop + "px");
}
+ private static Set navigationKeyCodes = new HashSet();
+ static {
+ navigationKeyCodes.add(KeyCodes.KEY_DOWN);
+ navigationKeyCodes.add(KeyCodes.KEY_UP);
+ navigationKeyCodes.add(KeyCodes.KEY_PAGEDOWN);
+ navigationKeyCodes.add(KeyCodes.KEY_PAGEUP);
+ navigationKeyCodes.add(KeyCodes.KEY_ENTER);
+ }
+
/*
* (non-Javadoc)
*
@@ -1338,34 +1405,39 @@ public class VFilterSelect extends Composite implements Paintable, Field,
*/
public void onKeyDown(KeyDownEvent event) {
if (enabled && !readonly) {
- if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- // Same reaction to enter no matter on whether the popup is open
- if (suggestionPopup.isAttached()) {
- filterOptions(currentPage);
- } else if (currentSuggestion != null
- && tb.getText().equals(
- currentSuggestion.getReplacementString())) {
- // Retain behavior from #6686 by returning without stopping
- // propagation if there's nothing to do
- return;
- }
- if (currentSuggestions.size() == 1 && !allowNewItem) {
- // If there is only one suggestion, select that
- suggestionPopup.menu.selectItem(suggestionPopup.menu
- .getItems().get(0));
- }
- suggestionPopup.menu.doSelectedItemAction();
+ int keyCode = event.getNativeKeyCode();
+ debug("key down: " + keyCode);
+ if (waitingForFilteringResponse
+ && navigationKeyCodes.contains(keyCode)) {
+ /*
+ * Keyboard navigation events should not be handled while we are
+ * waiting for a response. This avoids flickering, disappearing
+ * items, wrongly interpreted responses and more.
+ */
+ debug("Ignoring " + keyCode
+ + " because we are waiting for a filtering response");
+ DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
return;
- } else if (suggestionPopup.isAttached()) {
+ }
+
+ if (suggestionPopup.isAttached()) {
+ debug("Keycode " + keyCode + " target is popup");
popupKeyDown(event);
} else {
+ debug("Keycode " + keyCode + " target is text field");
inputFieldKeyDown(event);
}
}
}
+ private void debug(String string) {
+ if (enableDebug) {
+ VConsole.error(string);
+ }
+ }
+
/**
* Triggered when a key is pressed in the text box
*
@@ -1378,17 +1450,31 @@ public class VFilterSelect extends Composite implements Paintable, Field,
case KeyCodes.KEY_UP:
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
- if (!suggestionPopup.isAttached()) {
- // open popup as from gadget
- filterOptions(-1, "");
- lastFilter = "";
- tb.selectAll();
- }
+ // open popup as from gadget
+ filterOptions(-1, "");
+ lastFilter = "";
+ tb.selectAll();
break;
- case KeyCodes.KEY_TAB:
- if (suggestionPopup.isAttached()) {
- filterOptions(currentPage, tb.getText());
+ case KeyCodes.KEY_ENTER:
+ /*
+ * This only handles the case when new items is allowed, a text is
+ * entered, the popup opener button is clicked to close the popup
+ * and enter is then pressed (see #7560).
+ */
+ if (!allowNewItem) {
+ return;
}
+
+ if (currentSuggestion != null
+ && tb.getText().equals(
+ currentSuggestion.getReplacementString())) {
+ // Retain behavior from #6686 by returning without stopping
+ // propagation if there's nothing to do
+ return;
+ }
+ suggestionPopup.menu.doSelectedItemAction();
+
+ event.stopPropagation();
break;
}
@@ -1406,11 +1492,15 @@ public class VFilterSelect extends Composite implements Paintable, Field,
switch (event.getNativeKeyCode()) {
case KeyCodes.KEY_DOWN:
suggestionPopup.selectNextItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
+ .getSelectedItem());
DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
break;
case KeyCodes.KEY_UP:
suggestionPopup.selectPrevItem();
+ suggestionPopup.menu.setKeyboardSelectedItem(suggestionPopup.menu
+ .getSelectedItem());
DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
event.stopPropagation();
break;
@@ -1427,12 +1517,43 @@ public class VFilterSelect extends Composite implements Paintable, Field,
event.stopPropagation();
break;
case KeyCodes.KEY_TAB:
- if (suggestionPopup.isAttached()) {
- tabPressed = true;
- filterOptions(currentPage);
- }
+ tabPressedWhenPopupOpen = true;
+ filterOptions(currentPage);
// onBlur() takes care of the rest
break;
+ case KeyCodes.KEY_ESCAPE:
+ reset();
+ event.stopPropagation();
+ break;
+ case KeyCodes.KEY_ENTER:
+ if (suggestionPopup.menu.getKeyboardSelectedItem() == null) {
+ /*
+ * Nothing selected using up/down. Happens e.g. when entering a
+ * text (causes popup to open) and then pressing enter.
+ */
+ if (!allowNewItem) {
+ /*
+ * New items are not allowed: If there is only one
+ * suggestion, select that. Otherwise do nothing.
+ */
+ if (currentSuggestions.size() == 1) {
+ onSuggestionSelected(currentSuggestions.get(0));
+ }
+ } else {
+ // Handle addition of new items.
+ suggestionPopup.menu.doSelectedItemAction();
+ }
+ } else {
+ /*
+ * Get the suggestion that was navigated to using up/down.
+ */
+ currentSuggestion = ((FilterSelectSuggestion) suggestionPopup.menu
+ .getKeyboardSelectedItem().getCommand());
+ onSuggestionSelected(currentSuggestion);
+ }
+
+ event.stopPropagation();
+ break;
}
}
@@ -1455,10 +1576,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
case KeyCodes.KEY_UP:
case KeyCodes.KEY_PAGEDOWN:
case KeyCodes.KEY_PAGEUP:
- ; // NOP
- break;
case KeyCodes.KEY_ESCAPE:
- reset();
+ ; // NOP
break;
default:
if (textInputEnabled) {
@@ -1617,8 +1736,8 @@ public class VFilterSelect extends Composite implements Paintable, Field,
focused = false;
if (!readonly) {
// much of the TAB handling takes place here
- if (tabPressed) {
- tabPressed = false;
+ if (tabPressedWhenPopupOpen) {
+ tabPressedWhenPopupOpen = false;
suggestionPopup.menu.doSelectedItemAction();
suggestionPopup.hide();
} else if (!suggestionPopup.isAttached()
diff --git a/src/com/vaadin/ui/Select.java b/src/com/vaadin/ui/Select.java
index 7b6fc07e5b..439efdb864 100644
--- a/src/com/vaadin/ui/Select.java
+++ b/src/com/vaadin/ui/Select.java
@@ -148,7 +148,7 @@ public class Select extends AbstractSelect implements AbstractSelect.Filtering,
}
// Adds the required attribute
- if (isRequired()) {
+ if (!isReadOnly() && isRequired()) {
target.addAttribute("required", true);
}
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxFiltering.html b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxFiltering.html
new file mode 100644
index 0000000000..3de221871d
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxFiltering.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+New Test
+
+
+
+
+New Test
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxSlow?restartApplication
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxSlow::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ 92,19
+
+
+ enterCharacter
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxSlow::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ 1
+
+
+ screenCapture
+
+ filter-no-match
+
+
+ enterCharacter
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxSlow::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ Item 12
+
+
+ screenCapture
+
+ filter-11-matches-paging
+
+
+ screenCapture
+
+ filter-11-matches-page-2
+
+
+ enterCharacter
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxSlow::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ Item 100
+
+
+ screenCapture
+
+ filter-2-matches-no-paging
+
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.html b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.html
new file mode 100644
index 0000000000..6d18c60038
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+New Test
+
+
+
+
+New Test
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxIdenticalItems?restartApplication
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ 66,8
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ enter
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::PID_SLog_row_0
+ 1. Item one-1 selected
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ enter
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::PID_SLog_row_0
+ 2. Item one-2 selected
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ down
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ enter
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::PID_SLog_row_0
+ 3. Item two selected
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ up
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ up
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ up
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VFilterSelect[0]/domChild[0]
+ enter
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxIdenticalItems::PID_SLog_row_0
+ 4. Item one-1 selected
+
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.java b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.java
new file mode 100644
index 0000000000..dcd4af58ad
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxIdenticalItems.java
@@ -0,0 +1,54 @@
+package com.vaadin.tests.components.combobox;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.ComboBox;
+
+public class ComboBoxIdenticalItems extends TestBase {
+
+ private Log log = new Log(5);
+
+ @Override
+ public void setup() {
+ final ComboBox select = new ComboBox("ComboBox");
+ select.addContainerProperty("caption", String.class, null);
+ Item item = select.addItem("one-1");
+ item.getItemProperty("caption").setValue("One");
+ item = select.addItem("one-2");
+ item.getItemProperty("caption").setValue("One");
+ item = select.addItem("two");
+ item.getItemProperty("caption").setValue("Two");
+ select.setItemCaptionPropertyId("caption");
+ select.setNullSelectionAllowed(false);
+ select.setImmediate(true);
+ select.addListener(new Property.ValueChangeListener() {
+ private static final long serialVersionUID = -7932700771673919620L;
+
+ public void valueChange(ValueChangeEvent event) {
+ log.log("Item " + select.getValue() + " selected");
+
+ }
+ });
+
+ addComponent(log);
+ addComponent(select);
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Keyboard selecting of a value is broken in combobox if two "
+ + "items have the same caption. The first item's id is \"One-1\" "
+ + "while the second one is \"One-2\". Selecting with mouse works "
+ + "as expected but selecting with keyboard always returns the "
+ + "object \"One-1\".";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.htm b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.htm
new file mode 100644
index 0000000000..f943f912e4
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.htm
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+New Test
+
+
+
+
+New Test
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxInPopup?restartApplication
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxInPopup::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]/domChild[1]
+ 12,13
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxInPopup::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item1
+ Yes
+
+
+ pressSpecialKey
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxInPopup::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item1
+ esc
+
+
+
+ assertText
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxInPopup::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]
+ A combo box
+
+
+ assertElementNotPresent
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxInPopup::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item1
+
+
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.java b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.java
new file mode 100644
index 0000000000..8160fb576e
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxInPopup.java
@@ -0,0 +1,50 @@
+package com.vaadin.tests.components.combobox;
+
+import com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Window;
+
+public class ComboBoxInPopup extends TestBase {
+
+ @Override
+ protected void setup() {
+ final Window w = new Window();
+ w.getContent().setSizeUndefined();
+ w.addComponent(createComboBox());
+ Button close = new Button("Close window", new Button.ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ w.getParent().removeWindow(w);
+ }
+ });
+ close.setClickShortcut(KeyCode.ESCAPE, null);
+ w.addComponent(close);
+
+ getLayout().getWindow().addWindow(w);
+
+ }
+
+ private Component createComboBox() {
+ ComboBox cb = new ComboBox("A combo box");
+
+ cb.addItem("Yes");
+ cb.addItem("No");
+ cb.addItem("Maybe");
+ return cb;
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Escape is a shortcut for the close button. Pressing escape when the popup is open should cause only the popup to close, not the window.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 6978;
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxNullItem.html b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxNullItem.html
new file mode 100644
index 0000000000..3dc31f4a98
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxNullItem.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+New Test
+
+
+
+
+New Test
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxNavigation?restartApplication&theme=chameleon
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]/domChild[1]
+ 5,9
+
+
+ screenCapture
+
+ combobox-with-null-item-chameleon
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxNavigation?restartApplication&theme=runo
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]/domChild[1]
+ 5,9
+
+
+ screenCapture
+
+ combobox-with-null-item-runo
+
+
+ open
+ /run/com.vaadin.tests.components.combobox.ComboBoxNavigation?restartApplication&theme=reindeer
+
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboBoxNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]/domChild[1]
+ 5,9
+
+
+ screenCapture
+
+ combobox-with-null-item-reindeer
+
+
+
+
+
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxSlow.java b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxSlow.java
new file mode 100644
index 0000000000..15742cc783
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/combobox/ComboBoxSlow.java
@@ -0,0 +1,55 @@
+package com.vaadin.tests.components.combobox;
+
+import java.util.Map;
+
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.ComboBox;
+
+public class ComboBoxSlow extends TestBase {
+
+ private Log log = new Log(5);
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 7949;
+ }
+
+ @Override
+ protected String getDescription() {
+ return "The ComboBox artificially introduces a server delay to more easily spot problems";
+ }
+
+ public class SlowComboBox extends ComboBox {
+ @Override
+ public void changeVariables(Object source, Map variables) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ super.changeVariables(source, variables);
+ }
+ }
+
+ @Override
+ protected void setup() {
+ addComponent(log);
+ final SlowComboBox cb = new SlowComboBox();
+ cb.setImmediate(true);
+ for (int i = 0; i <= 1000; i++) {
+ cb.addItem("Item " + i);
+ }
+ cb.addListener(new ValueChangeListener() {
+
+ public void valueChange(ValueChangeEvent event) {
+ log.log("Value changed to " + cb.getValue());
+
+ }
+ });
+ addComponent(cb);
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/combobox/Comboboxes.html b/tests/testbench/com/vaadin/tests/components/combobox/Comboboxes.html
index 1affc9a8a9..4aa84a9453 100644
--- a/tests/testbench/com/vaadin/tests/components/combobox/Comboboxes.html
+++ b/tests/testbench/com/vaadin/tests/components/combobox/Comboboxes.html
@@ -250,30 +250,39 @@
mouseClick
- vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Readonly/domChild[0]
- 26,10
+ vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Required/domChild[0]
+ 12,-8
- waitForVaadin
-
-
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Error indicators/domChild[0]
+ 7,-8
screenCapture
- error
+ readonly-required
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Readonly/domChild[0]
+ 26,10
+
+
+ mouseClick
+ vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Required/domChild[0]
+ 3,-7
mouseClick
vaadin=runcomvaadintestscomponentscomboboxComboboxes::PID_Scheckboxaction-Error indicators/domChild[0]
- 67,8
+ 7,-8
- waitForVaadin
-
+ screenCapture
+ error
-