From f743e042f03f2c72d2f71507e475e3d26109024a Mon Sep 17 00:00:00 2001 From: Anthony Guerreiro Date: Wed, 2 Jul 2014 11:58:34 +0300 Subject: Fix NativeButton clickEvent coordinates in IE11 (#14022) Two clicks were being triggered for IE11, the first with coordinates (0,0) and the second with the correct coordinates. Change-Id: I6f0feb520710b254eac6542f082a5012de2c5f85 --- client/src/com/vaadin/client/ui/VNativeButton.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'client') diff --git a/client/src/com/vaadin/client/ui/VNativeButton.java b/client/src/com/vaadin/client/ui/VNativeButton.java index 93d8d958d6..8e0dd2bce1 100644 --- a/client/src/com/vaadin/client/ui/VNativeButton.java +++ b/client/src/com/vaadin/client/ui/VNativeButton.java @@ -104,7 +104,9 @@ public class VNativeButton extends Button implements ClickHandler { } clickPending = false; } else if (event.getTypeInt() == Event.ONFOCUS) { - if (BrowserInfo.get().isIE() && clickPending) { + if (BrowserInfo.get().isIE() + && BrowserInfo.get().getBrowserMajorVersion() < 11 + && clickPending) { /* * The focus event will mess up IE and IE will not trigger the * mouse up event (which in turn triggers the click event) until -- cgit v1.2.3 From 9b19675dffec603bc7e8fe6d973ed4edafaff136 Mon Sep 17 00:00:00 2001 From: Bogdan Udrescu Date: Tue, 8 Jul 2014 09:58:45 +0300 Subject: Bottom component click scroll up the parent panel in a window (#12943) Due to old fix for (#11994) the v-scrollable div of the window would expand to 110% of its size then immediately back to the original size. The first action, expanding the v-scrollable to 110% would decrease the scrollTop value of our panel, while increasing its height. When the revert back action would set the v-scrollable to its own size, the panel's scrollTop would remain decreased, causing the scroll bar to move up, hiding the ~10% at the bottom. Fixed by calling Util.runWebkitOverflowAutoFix(); instead of changing the height. Change-Id: I79eafd1f9500c2e4c10dadbfc7100608c0732e04 --- .../html-tests/BottomComponentScrollsUp.html | 96 +++++++++++++++++++ .../com/vaadin/client/ApplicationConnection.java | 1 - client/src/com/vaadin/client/Util.java | 18 ++++ client/src/com/vaadin/client/ui/VScrollTable.java | 23 +---- client/src/com/vaadin/client/ui/VWindow.java | 90 ++++++------------ .../com/vaadin/client/ui/table/TableConnector.java | 10 +- .../window/BottomComponentScrollsUp.java | 103 +++++++++++++++++++++ .../window/BottomComponentScrollsUpTest.java | 71 ++++++++++++++ 8 files changed, 323 insertions(+), 89 deletions(-) create mode 100644 WebContent/html-tests/BottomComponentScrollsUp.html create mode 100644 uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUp.java create mode 100644 uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUpTest.java (limited to 'client') diff --git a/WebContent/html-tests/BottomComponentScrollsUp.html b/WebContent/html-tests/BottomComponentScrollsUp.html new file mode 100644 index 0000000000..a264b38ba8 --- /dev/null +++ b/WebContent/html-tests/BottomComponentScrollsUp.html @@ -0,0 +1,96 @@ + + + +Bottom component scroll when focus - test with plain html to see the default behaviour + + + + + + + + +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 6abcdac487..5fcb2070ec 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -66,7 +66,6 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; -import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index f175bbe714..f9243dafe9 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -468,6 +468,24 @@ public class Util { return detectedScrollbarSize; } + /** + * Defers the execution of {@link #runWebkitOverflowAutoFix(Element)} + * + * @since + * @param elem + * with overflow auto + */ + public static void runWebkitOverflowAutoFixDeferred(final Element elem) { + Scheduler.get().scheduleDeferred(new Command() { + + @Override + public void execute() { + Util.runWebkitOverflowAutoFix(elem); + } + }); + + } + /** * Run workaround for webkits overflow auto issue. * diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index d88f7426ef..59645aa6d3 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -2251,13 +2251,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * Ensures the column alignments are correct at initial loading.
* (child components widths are correct) */ - Scheduler.get().scheduleDeferred(new Command() { - - @Override - public void execute() { - Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement()); - } - }); + Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement()); hadScrollBars = willHaveScrollbarz; } @@ -6720,13 +6714,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, Util.notifyParentOfSizeChange(VScrollTable.this, rendering); } } - Scheduler.get().scheduleDeferred(new Command() { - @Override - public void execute() { - Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement()); - } - }); + Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement()); forceRealignColumnHeaders(); } @@ -6863,13 +6852,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // We must run the fix as a deferred command to prevent it from // overwriting the scroll position with an outdated value, see // #7607. - Scheduler.get().scheduleDeferred(new Command() { - - @Override - public void execute() { - Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement()); - } - }); + Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement()); } triggerLazyColumnAdjustment(false); diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index 1cee727bc9..83a0001ad8 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.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 @@ -73,7 +73,7 @@ import com.vaadin.shared.ui.window.WindowRole; /** * "Sub window" component. - * + * * @author Vaadin Ltd */ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, @@ -295,7 +295,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Returns true if this window is the topmost VWindow - * + * * @return */ private boolean isActive() { @@ -437,7 +437,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * is prevented. *

* This message is not visible on the screen. - * + * * @param topMessage * String provided when the user navigates with Shift-Tab keys to * the top of the window @@ -452,7 +452,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * key is prevented. *

* This message is not visible on the screen. - * + * * @param bottomMessage * String provided when the user navigates with the Tab key to * the bottom of the window @@ -465,7 +465,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the message that is provided to users of assistive devices when the * user reaches the top of the window when leaving a window with the tab key * is prevented. - * + * * @return the top message */ public String getTabStopTopAssistiveText() { @@ -476,7 +476,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the message that is provided to users of assistive devices when the * user reaches the bottom of the window when leaving a window with the tab * key is prevented. - * + * * @return the bottom message */ public String getTabStopBottomAssistiveText() { @@ -554,41 +554,11 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /* * Shake up the DOM a bit to make the window shed unnecessary - * scrollbars and resize correctly afterwards. This resulting code - * took over a week to summon forth, and involved some pretty hairy - * black magic. Don't touch it unless you know what you're doing! - * Fixes ticket #11994 + * scrollbars and resize correctly afterwards. The version fixing + * ticket #11994 which was changing the size to 110% was replaced + * with this due to ticket #12943 */ - Scheduler.get().scheduleFinally(new ScheduledCommand() { - @Override - public void execute() { - final com.google.gwt.dom.client.Element scrollable = contents - .getFirstChildElement(); - - // Adjusting the width or height may change the scroll - // position, so store the current position - int horizontalScrollPosition = scrollable.getScrollLeft(); - int verticalScrollPosition = scrollable.getScrollTop(); - - final String oldWidth = scrollable.getStyle().getWidth(); - final String oldHeight = scrollable.getStyle().getHeight(); - - scrollable.getStyle().setWidth(110, Unit.PCT); - scrollable.getOffsetWidth(); - scrollable.getStyle().setProperty("width", oldWidth); - - scrollable.getStyle().setHeight(110, Unit.PCT); - scrollable.getOffsetHeight(); - scrollable.getStyle().setProperty("height", oldHeight); - - // Restore the scroll position - scrollable.setScrollLeft(horizontalScrollPosition); - scrollable.setScrollTop(verticalScrollPosition); - - updateContentsSize(); - positionOrSizeUpdated(); - } - }); + Util.runWebkitOverflowAutoFix(contents.getFirstChildElement()); } } @@ -616,7 +586,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Sets the closable state of the window. Additionally hides/shows the close * button according to the new state. - * + * * @param closable * true if the window can be closed by the user */ @@ -638,7 +608,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Returns the closable state of the sub window. If the sub window is * closable a decoration (typically an X) is shown to the user. By clicking * on the X the user can close the window. - * + * * @return true if the sub window is closable */ protected boolean isClosable() { @@ -902,7 +872,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Setter for the text for assistive devices the window caption is prefixed * with. - * + * * @param assistivePrefix * the assistivePrefix to set */ @@ -913,7 +883,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Getter for the text for assistive devices the window caption is prefixed * with. - * + * * @return the assistivePrefix */ public String getAssistivePrefix() { @@ -923,7 +893,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Setter for the text for assistive devices the window caption is postfixed * with. - * + * * @param assistivePostfix * the assistivePostfix to set */ @@ -934,7 +904,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Getter for the text for assistive devices the window caption is postfixed * with. - * + * * @return the assistivePostfix */ public String getAssistivePostfix() { @@ -1086,14 +1056,14 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * TODO check if we need to support this with touch based devices. - * + * * Checks if the cursor was inside the browser content area when the event * happened. - * + * * @param event * The event to be checked * @return true, if the cursor is inside the browser content area - * + * * false, otherwise */ private boolean cursorInsideBrowserContentArea(Event event) { @@ -1382,7 +1352,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * assistive devices when it is opened. *

* When the provided array is empty, an existing description is removed. - * + * * @param connectors * with the connectors of the widgets to use as description */ @@ -1420,7 +1390,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the connectors that are used as assistive description. Text * contained in these connectors will be read by assistive devices when the * window is opened. - * + * * @return list of previously set connectors */ public List getAssistiveDescription() { @@ -1429,14 +1399,14 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Sets the WAI-ARIA role the window. - * + * * This role defines how an assistive device handles a window. Available * roles are alertdialog and dialog (@see Roles * Model). - * + * * The default role is dialog. - * + * * @param role * WAI-ARIA role to set for the window */ @@ -1455,7 +1425,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * The value of the parameter doTabStop is stored and used for non-modal * windows. For modal windows, the handlers are always registered, while * preserving the stored value. - * + * * @param doTabStop * true to prevent leaving the window, false to allow leaving the * window for non modal windows @@ -1472,9 +1442,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Adds a Handler for when user moves the window. - * + * * @since 7.1.9 - * + * * @return {@link HandlerRegistration} used to remove the handler */ public HandlerRegistration addMoveHandler(WindowMoveHandler handler) { diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index 017d1d1024..d37fd36522 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -21,7 +21,6 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Position; -import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; @@ -210,13 +209,8 @@ public class TableConnector extends AbstractHasComponentsConnector implements // by changing overflows as the length of the contents // *shouldn't* have changed (unless the number of rows // or the height of the widget has also changed) - Scheduler.get().scheduleDeferred(new Command() { - @Override - public void execute() { - Util.runWebkitOverflowAutoFix(getWidget().scrollBodyPanel - .getElement()); - } - }); + Util.runWebkitOverflowAutoFixDeferred(getWidget().scrollBodyPanel + .getElement()); } } else { getWidget().initializeRows(uidl, rowData); diff --git a/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUp.java b/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUp.java new file mode 100644 index 0000000000..2c5e415408 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUp.java @@ -0,0 +1,103 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.window; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Panel; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; + +/** + * Reproducing bug #12943 where an action on a Button or ComboBox placed at the + * bottom of a window in a scroll panel, will scroll up the parent panel. + * + * This was due to the fact that with the state confirmation notification from + * the server, the window.setVisible would be call again, and the hack that + * solved the scrollbars in a window (#11994) would cause the our bug. + * + * @since + * @author Vaadin Ltd + */ +@SuppressWarnings("serial") +public class BottomComponentScrollsUp extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Button b = new Button("Open window"); + addComponent(b); + b.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + openWindow(); + } + + }); + + openWindow(); + } + + private void openWindow() { + Window w = new Window(); + w.setWidth("300px"); + w.setHeight("300px"); + w.center(); + + Panel p = createPanel(); + p.setSizeFull(); + + w.setContent(p); + + addWindow(w); + } + + private Panel createPanel() { + Panel p = new Panel(); + + VerticalLayout content = new VerticalLayout(); + p.setContent(content); + content.setHeight("500px"); + + List items = new ArrayList(); + items.add("1"); + items.add("2"); + items.add("3"); + + Button button = new Button("Press me"); + content.addComponent(button); + content.setComponentAlignment(button, Alignment.BOTTOM_CENTER); + return p; + } + + @Override + protected String getTestDescription() { + return "Interacting with a component at the bottom of scrollable panel within a subwindow scrolls up"; + } + + @Override + protected Integer getTicketNumber() { + return 12943; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUpTest.java b/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUpTest.java new file mode 100644 index 0000000000..3d0da2677b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/BottomComponentScrollsUpTest.java @@ -0,0 +1,71 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.window; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Automatic test for fix for #12943. + * + * While testing without the fix, the test failed on both Chrome and PhantomJS. + * + * @since + * @author Vaadin Ltd + */ +public class BottomComponentScrollsUpTest extends MultiBrowserTest { + + @Override + public void setup() throws Exception { + super.setup(); + + openTestURL(); + } + + @Test + public void windowScrollTest() throws IOException, InterruptedException { + TestBenchElement panelScrollable = (TestBenchElement) getDriver() + .findElement(By.className("v-panel-content")); + Dimension panelScrollableSize = panelScrollable.getSize(); + + WebElement verticalLayout = panelScrollable.findElement(By + .className("v-verticallayout")); + Dimension verticalLayoutSize = verticalLayout.getSize(); + + panelScrollable.scroll(verticalLayoutSize.height); + + WebElement button = verticalLayout + .findElement(By.className("v-button")); + + button.click(); + + // Loose the focus from the button. + new Actions(getDriver()) + .moveToElement(panelScrollable, panelScrollableSize.width / 2, + panelScrollableSize.height / 2).click().build() + .perform(); + + compareScreen("window"); + } +} -- cgit v1.2.3 From 28702c006fe124988c03c99eea4e2d609407e47e Mon Sep 17 00:00:00 2001 From: Antti Tanhuanpää Date: Mon, 30 Jun 2014 17:07:50 +0300 Subject: Add scrollbars to ComboBox suggestion popup if low on screen estate (#11929) Change-Id: Idfeb20a385fc68c6527f1947bdbf238d9d4af918 --- client/src/com/vaadin/client/ui/VFilterSelect.java | 381 ++++++++++++++------- .../client/ui/combobox/ComboBoxConnector.java | 39 ++- .../src/com/vaadin/client/ui/menubar/MenuBar.java | 108 +++++- .../components/combobox/ComboBoxOnSmallScreen.java | 76 ++++ .../combobox/ComboBoxOnSmallScreenTest.java | 84 +++++ .../com/vaadin/tests/components/combobox/fi.png | Bin 0 -> 25094 bytes .../vaadin/tests/components/combobox/fi_small.png | Bin 0 -> 3576 bytes 7 files changed, 552 insertions(+), 136 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreen.java create mode 100644 uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreenTest.java create mode 100644 uitest/src/com/vaadin/tests/components/combobox/fi.png create mode 100644 uitest/src/com/vaadin/tests/components/combobox/fi_small.png (limited to 'client') diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index 7f67c39500..46f90c07fa 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -62,6 +62,7 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ComputedStyle; import com.vaadin.client.ConnectorMap; import com.vaadin.client.Focusable; import com.vaadin.client.UIDL; @@ -252,7 +253,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 @@ -264,10 +265,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, final Collection currentSuggestions, final int currentPage, final int totalSuggestions) { - if (enableDebug) { - debug("VFS.SP: showSuggestions(" + currentSuggestions + ", " - + currentPage + ", " + totalSuggestions + ")"); - } + debug("VFS.SP: showSuggestions(" + currentSuggestions + ", " + + currentPage + ", " + totalSuggestions + ")"); /* * We need to defer the opening of the popup so that the parent DOM @@ -316,8 +315,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, status.setInnerText(""); } // We don't need to show arrows or statusbar if there is - // only one - // page + // only one page if (totalSuggestions <= pageLength || pageLength == 0) { setPagingEnabled(false); } else { @@ -346,7 +344,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) { @@ -366,7 +364,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) { @@ -391,18 +389,13 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, */ public void selectNextItem() { debug("VFS.SP: selectNextItem()"); - final MenuItem cur = menu.getSelectedItem(); - final int index = 1 + menu.getItems().indexOf(cur); + + final int index = menu.getSelectedIndex() + 1; if (menu.getItems().size() > index) { - final MenuItem newSelectedItem = menu.getItems().get(index); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - lastFilter.length()); - - } else if (hasNextPage()) { - selectPopupItemWhenResponseIsReceived = Select.FIRST; - filterOptions(currentPage + 1, lastFilter); + selectItem(menu.getItems().get(index)); + + } else { + selectNextPage(); } } @@ -411,29 +404,61 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, */ public void selectPrevItem() { debug("VFS.SP: selectPrevItem()"); - final MenuItem cur = menu.getSelectedItem(); - final int index = -1 + menu.getItems().indexOf(cur); + + final int index = menu.getSelectedIndex() - 1; if (index > -1) { - final MenuItem newSelectedItem = menu.getItems().get(index); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - lastFilter.length()); + selectItem(menu.getItems().get(index)); + } else if (index == -1) { - if (currentPage > 0) { - selectPopupItemWhenResponseIsReceived = Select.LAST; - filterOptions(currentPage - 1, lastFilter); - } + selectPrevPage(); + } else { - final MenuItem newSelectedItem = menu.getItems().get( - menu.getItems().size() - 1); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - lastFilter.length()); + selectItem(menu.getItems().get(menu.getItems().size() - 1)); } } + /** + * Select the first item of the suggestions list popup. + * + * @since + */ + public void selectFirstItem() { + debug("VFS.SP: selectFirstItem()"); + selectItem(menu.getFirstItem()); + } + + /** + * Select the last item of the suggestions list popup. + * + * @since + */ + public void selectLastItem() { + debug("VFS.SP: selectLastItem()"); + selectItem(menu.getLastItem()); + } + + /* + * Sets the selected item in the popup menu. + */ + private void selectItem(final MenuItem newSelectedItem) { + menu.selectItem(newSelectedItem); + + String text = newSelectedItem != null ? newSelectedItem.getText() + : ""; + + // Set the icon. + FilterSelectSuggestion suggestion = (FilterSelectSuggestion) newSelectedItem + .getCommand(); + setSelectedItemIcon(suggestion.getIconUri()); + + // Set the text. + tb.setText(text); + tb.setSelectionRange(lastFilter.length(), text.length() + - lastFilter.length()); + + menu.updateKeyboardSelectedItem(); + } + /* * Using a timer to scroll up or down the pages so when we receive lots * of consecutive mouse wheel events the pages does not flicker. @@ -486,17 +511,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } } - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt - * .user.client.Event) - */ - @Override public void onBrowserEvent(Event event) { debug("VFS.SP: onBrowserEvent()"); + if (event.getTypeInt() == Event.ONCLICK) { final Element target = DOM.eventGetTarget(event); if (target == up || target == DOM.getChild(up, 0)) { @@ -504,12 +522,24 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } else if (target == down || target == DOM.getChild(down, 0)) { lazyPageScroller.scrollDown(); } + } else if (event.getTypeInt() == Event.ONMOUSEWHEEL) { - int velocity = event.getMouseWheelVelocityY(); - if (velocity > 0) { - lazyPageScroller.scrollDown(); - } else { - lazyPageScroller.scrollUp(); + + boolean scrollNotActive = !menu.isScrollActive(); + + debug("VFS.SP: onBrowserEvent() scrollNotActive: " + + scrollNotActive); + + if (scrollNotActive) { + int velocity = event.getMouseWheelVelocityY(); + + debug("VFS.SP: onBrowserEvent() velocity: " + velocity); + + if (velocity > 0) { + lazyPageScroller.scrollDown(); + } else { + lazyPageScroller.scrollUp(); + } } } @@ -525,7 +555,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? */ @@ -546,32 +576,29 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, isPagingEnabled = paging; } - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition - * (int, int) - */ - @Override public void setPosition(int offsetWidth, int offsetHeight) { - debug("VFS.SP: setPosition()"); + debug("VFS.SP: setPosition(" + offsetWidth + ", " + offsetHeight + + ")"); - int top = -1; - int left = -1; + int top = topPosition; + int left = getPopupLeft(); // reset menu size and retrieve its "natural" size menu.setHeight(""); - if (currentPage > 0) { + if (currentPage > 0 && !hasNextPage()) { // fix height to avoid height change when getting to last page menu.fixHeightTo(pageLength); } - offsetHeight = getOffsetHeight(); + final int desiredHeight = offsetHeight = getOffsetHeight(); final int desiredWidth = getMainWidth(); + + debug("VFS.SP: desired[" + desiredWidth + ", " + desiredHeight + + "]"); + Element menuFirstChild = menu.getElement().getFirstChildElement(); - int naturalMenuWidth = menuFirstChild.getOffsetWidth(); + final int naturalMenuWidth = menuFirstChild.getOffsetWidth(); if (popupOuterPadding == -1) { popupOuterPadding = Util.measureHorizontalPaddingAndBorder( @@ -581,7 +608,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, if (naturalMenuWidth < desiredWidth) { menu.setWidth((desiredWidth - popupOuterPadding) + "px"); menuFirstChild.getStyle().setWidth(100, Unit.PCT); - naturalMenuWidth = desiredWidth; } if (BrowserInfo.get().isIE()) { @@ -589,48 +615,72 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * IE requires us to specify the width for the container * element. Otherwise it will be 100% wide */ - int rootWidth = naturalMenuWidth - popupOuterPadding; + int rootWidth = Math.max(desiredWidth, naturalMenuWidth) + - popupOuterPadding; getContainerElement().getStyle().setWidth(rootWidth, Unit.PX); } - if (offsetHeight + getPopupTop() > Window.getClientHeight() - + Window.getScrollTop()) { + final int vfsHeight = VFilterSelect.this.getOffsetHeight(); + final int spaceAvailableAbove = top - vfsHeight; + final int spaceAvailableBelow = Window.getClientHeight() - top; + if (spaceAvailableBelow < offsetHeight + && spaceAvailableBelow < spaceAvailableAbove) { // popup on top of input instead - top = getPopupTop() - offsetHeight - - VFilterSelect.this.getOffsetHeight(); + top -= offsetHeight + vfsHeight; if (top < 0) { + offsetHeight += top; top = 0; } } else { - top = getPopupTop(); - /* - * Take popup top margin into account. getPopupTop() returns the - * top value including the margin but the value we give must not - * include the margin. - */ - int topMargin = (top - topPosition); - top -= topMargin; + offsetHeight = Math.min(offsetHeight, spaceAvailableBelow); } // fetch real width (mac FF bugs here due GWT popups overflow:auto ) offsetWidth = menuFirstChild.getOffsetWidth(); - if (offsetWidth + getPopupLeft() > Window.getClientWidth() - + Window.getScrollLeft()) { + + if (offsetHeight < desiredHeight) { + int menuHeight = offsetHeight; + if (isPagingEnabled) { + menuHeight -= up.getOffsetHeight() + down.getOffsetHeight() + + status.getOffsetHeight(); + } else { + final ComputedStyle s = new ComputedStyle(menu.getElement()); + menuHeight -= s.getIntProperty("marginBottom") + + s.getIntProperty("marginTop"); + } + + // If the available page height is really tiny then this will be + // negative and an exception will be thrown on setHeight. + int menuElementHeight = menu.getItemOffsetHeight(); + if (menuHeight < menuElementHeight) { + menuHeight = menuElementHeight; + } + + menu.setHeight(menuHeight + "px"); + + final int naturalMenuWidthPlusScrollBar = naturalMenuWidth + + Util.getNativeScrollbarSize(); + if (offsetWidth < naturalMenuWidthPlusScrollBar) { + menu.setWidth(naturalMenuWidthPlusScrollBar + "px"); + } + } + + if (offsetWidth + left > Window.getClientWidth()) { left = VFilterSelect.this.getAbsoluteLeft() - + VFilterSelect.this.getOffsetWidth() - + Window.getScrollLeft() - offsetWidth; + + VFilterSelect.this.getOffsetWidth() - offsetWidth; if (left < 0) { left = 0; + menu.setWidth(Window.getClientWidth() + "px"); } - } else { - left = getPopupLeft(); } + setPopupPosition(left, top); + menu.scrollSelectionIntoView(); } /** * Was the popup just closed? - * + * * @return true if popup was just closed */ public boolean isJustClosed() { @@ -659,7 +709,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 @@ -723,23 +773,34 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, super(true); debug("VFS.SM: constructor()"); addDomHandler(this, LoadEvent.getType()); + + setScrollEnabled(true); } /** * Fixes menus height to use same space as full page would use. Needed - * to avoid height changes when quickly "scrolling" to last page + * to avoid height changes when quickly "scrolling" to last page. + */ + public void fixHeightTo(int pageItemsCount) { + setHeight(getPreferredHeight(pageItemsCount)); + } + + /* + * Gets the preferred height of the menu including pageItemsCount items. */ - public void fixHeightTo(int pagelenth) { + String getPreferredHeight(int pageItemsCount) { if (currentSuggestions.size() > 0) { - final int pixels = pagelenth * (getOffsetHeight() - 2) - / currentSuggestions.size(); - setHeight((pixels + 2) + "px"); + final int pixels = (getPreferredHeight() / currentSuggestions + .size()) * pageItemsCount; + return pixels + "px"; + } else { + return ""; } } /** * Sets the suggestions rendered in the menu - * + * * @param suggestions * The suggestions to be rendered in the menu */ @@ -789,6 +850,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, client.updateVariable(paintableId, "page", 0, false); client.updateVariable(paintableId, "selected", new String[] {}, immediate); + afterUpdateClientVariables(); + suggestionPopup.hide(); return; } @@ -837,6 +900,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, lastNewItemString = enteredItemValue; client.updateVariable(paintableId, "newitem", enteredItemValue, immediate); + afterUpdateClientVariables(); } } else if (item != null && !"".equals(lastFilter) @@ -909,26 +973,75 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } - public void selectFirstItem() { - debug("VFS.SM: selectFirstItem()"); - MenuItem firstItem = getItems().get(0); - selectItem(firstItem); - } - private MenuItem getKeyboardSelectedItem() { return keyboardSelectedItem; } - public void setKeyboardSelectedItem(MenuItem firstItem) { - keyboardSelectedItem = firstItem; + public void setKeyboardSelectedItem(MenuItem menuItem) { + keyboardSelectedItem = menuItem; + } + + /** + * @deprecated use {@link SuggestionPopup#selectFirstItem()} instead. + */ + @Deprecated + public void selectFirstItem() { + debug("VFS.SM: selectFirstItem()"); + MenuItem firstItem = getItems().get(0); + selectItem(firstItem); } + /** + * @deprecated use {@link SuggestionPopup#selectLastItem()} instead. + */ + @Deprecated public void selectLastItem() { debug("VFS.SM: selectLastItem()"); List items = getItems(); MenuItem lastItem = items.get(items.size() - 1); selectItem(lastItem); } + + /* + * Sets the keyboard item as the current selected one. + */ + void updateKeyboardSelectedItem() { + setKeyboardSelectedItem(getSelectedItem()); + } + + /* + * Gets the height of one menu item. + */ + int getItemOffsetHeight() { + List items = getItems(); + return items != null && items.size() > 0 ? items.get(0) + .getOffsetHeight() : 0; + } + + /* + * Gets the width of one menu item. + */ + int getItemOffsetWidth() { + List items = getItems(); + return items != null && items.size() > 0 ? items.get(0) + .getOffsetWidth() : 0; + } + + /** + * Returns true if the scroll is active on the menu element or if the + * menu currently displays the last page with less items then the + * maximum visibility (in which case the scroll is not active, but the + * scroll is active for any other page in general). + */ + @Override + public boolean isScrollActive() { + String height = getElement().getStyle().getHeight(); + String preferredHeight = getPreferredHeight(pageLength); + + return !(height == null || height.length() == 0 || height + .equals(preferredHeight)); + } + } /** @@ -1273,10 +1386,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * Whether to send the options request immediately */ private void filterOptions(int page, String filter, boolean immediate) { - if (enableDebug) { - debug("VFS: filterOptions(" + page + ", " + filter + ", " - + immediate + ")"); - } + debug("VFS: filterOptions(" + page + ", " + filter + ", " + immediate + + ")"); + if (filter.equals(lastFilter) && currentPage == page) { if (!suggestionPopup.isAttached()) { suggestionPopup.showSuggestions(currentSuggestions, @@ -1297,8 +1409,11 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, waitingForFilteringResponse = true; client.updateVariable(paintableId, "filter", filter, false); client.updateVariable(paintableId, "page", page, immediate); + afterUpdateClientVariables(); + lastFilter = filter; currentPage = page; + } /** For internal use only. May be removed or replaced in the future. */ @@ -1401,10 +1516,13 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, setPromptingOff(text); } setSelectedItemIcon(suggestion.getIconUri()); + if (!(newKey.equals(selectedOptionKey) || ("".equals(newKey) && selectedOptionKey == null))) { selectedOptionKey = newKey; client.updateVariable(paintableId, "selected", new String[] { selectedOptionKey }, immediate); + afterUpdateClientVariables(); + // currentPage = -1; // forget the page } suggestionPopup.hide(); @@ -1597,28 +1715,22 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, 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; case KeyCodes.KEY_PAGEDOWN: - if (hasNextPage()) { - filterOptions(currentPage + 1, lastFilter); - } + selectNextPage(); event.stopPropagation(); break; case KeyCodes.KEY_PAGEUP: - if (currentPage > 0) { - filterOptions(currentPage - 1, lastFilter); - } + selectPrevPage(); event.stopPropagation(); break; case KeyCodes.KEY_TAB: @@ -1664,6 +1776,26 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } + /* + * Show the prev page. + */ + private void selectPrevPage() { + if (currentPage > 0) { + filterOptions(currentPage - 1, lastFilter); + selectPopupItemWhenResponseIsReceived = Select.LAST; + } + } + + /* + * Show the next page. + */ + private void selectNextPage() { + if (hasNextPage()) { + filterOptions(currentPage + 1, lastFilter); + selectPopupItemWhenResponseIsReceived = Select.FIRST; + } + } + /** * Triggered when a key was depressed * @@ -1707,15 +1839,21 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, if (currentSuggestion != null) { String text = currentSuggestion.getReplacementString(); setPromptingOff(text); + setSelectedItemIcon(currentSuggestion.getIconUri()); + selectedOptionKey = currentSuggestion.key; + } else { if (focused || readonly || !enabled) { setPromptingOff(""); } else { setPromptingOn(); } + setSelectedItemIcon(null); + selectedOptionKey = null; } + lastFilter = ""; suggestionPopup.hide(); } @@ -1837,6 +1975,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, if (client.hasEventListeners(this, EventId.FOCUS)) { client.updateVariable(paintableId, EventId.FOCUS, "", true); + afterUpdateClientVariables(); } ComponentConnector connector = ConnectorMap.get(client).getConnector( @@ -1913,6 +2052,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, if (client.hasEventListeners(this, EventId.BLUR)) { client.updateVariable(paintableId, EventId.BLUR, "", true); + afterUpdateClientVariables(); } } @@ -2091,4 +2231,15 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, com.google.gwt.user.client.Element captionElement) { AriaHelper.bindCaption(tb, captionElement); } + + /* + * Anything that should be set after the client updates the server. + */ + private void afterUpdateClientVariables() { + // We need this here to be consistent with the all the calls. + // Then set your specific selection type only after + // client.updateVariable() method call. + selectPopupItemWhenResponseIsReceived = Select.NONE; + } + } diff --git a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java index 78505d034c..8feb349a9e 100644 --- a/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java +++ b/client/src/com/vaadin/client/ui/combobox/ComboBoxConnector.java @@ -28,7 +28,6 @@ import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VFilterSelect; import com.vaadin.client.ui.VFilterSelect.FilterSelectSuggestion; -import com.vaadin.client.ui.menubar.MenuItem; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.combobox.ComboBoxConstants; import com.vaadin.shared.ui.combobox.ComboBoxState; @@ -157,7 +156,15 @@ public class ComboBoxConnector extends AbstractFieldConnector implements } // handle selection (null or a single value) - if (uidl.hasVariable("selected")) { + if (uidl.hasVariable("selected") + + // In case we're switching page no need to update the selection as the + // selection process didn't finish. + // && getWidget().selectPopupItemWhenResponseIsReceived == + // VFilterSelect.Select.NONE + // + ) { + String[] selectedKeys = uidl.getStringArrayVariable("selected"); if (selectedKeys.length > 0) { performSelection(selectedKeys[0]); @@ -169,12 +176,16 @@ public class ComboBoxConnector extends AbstractFieldConnector implements if ((getWidget().waitingForFilteringResponse && getWidget().lastFilter .toLowerCase().equals(uidl.getStringVariable("filter"))) || popupOpenAndCleared) { + getWidget().suggestionPopup.showSuggestions( getWidget().currentSuggestions, getWidget().currentPage, getWidget().totalMatches); + getWidget().waitingForFilteringResponse = false; + if (!getWidget().popupOpenerClicked && getWidget().selectPopupItemWhenResponseIsReceived != VFilterSelect.Select.NONE) { + // we're paging w/ arrows Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override @@ -222,26 +233,16 @@ public class ComboBoxConnector extends AbstractFieldConnector implements */ private void navigateItemAfterPageChange() { if (getWidget().selectPopupItemWhenResponseIsReceived == VFilterSelect.Select.LAST) { - getWidget().suggestionPopup.menu.selectLastItem(); + getWidget().suggestionPopup.selectLastItem(); } else { - getWidget().suggestionPopup.menu.selectFirstItem(); + getWidget().suggestionPopup.selectFirstItem(); } - // This is used for paging so we update the keyboard selection - // variable as well. - MenuItem activeMenuItem = getWidget().suggestionPopup.menu - .getSelectedItem(); - getWidget().suggestionPopup.menu - .setKeyboardSelectedItem(activeMenuItem); - - // Update text field to contain the correct text - getWidget().setTextboxText(activeMenuItem.getText()); - getWidget().tb.setSelectionRange( - getWidget().lastFilter.length(), - activeMenuItem.getText().length() - - getWidget().lastFilter.length()); - - getWidget().selectPopupItemWhenResponseIsReceived = VFilterSelect.Select.NONE; // reset + // If you're in between 2 requests both changing the page back and + // forth, you don't want this here, instead you need it before any + // other request. + // getWidget().selectPopupItemWhenResponseIsReceived = + // VFilterSelect.Select.NONE; // reset } private void performSelection(String selectedKey) { diff --git a/client/src/com/vaadin/client/ui/menubar/MenuBar.java b/client/src/com/vaadin/client/ui/menubar/MenuBar.java index b00665e766..726defafd5 100644 --- a/client/src/com/vaadin/client/ui/menubar/MenuBar.java +++ b/client/src/com/vaadin/client/ui/menubar/MenuBar.java @@ -38,6 +38,7 @@ import java.util.List; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; @@ -74,6 +75,9 @@ import com.vaadin.client.ui.VOverlay; public class MenuBar extends Widget implements PopupListener { private final Element body; + private final Element table; + private final Element outer; + private final ArrayList items = new ArrayList(); private MenuBar parentMenu; private PopupPanel popup; @@ -98,7 +102,7 @@ public class MenuBar extends Widget implements PopupListener { public MenuBar(boolean vertical) { super(); - final Element table = DOM.createTable(); + table = DOM.createTable(); body = DOM.createTBody(); DOM.appendChild(table, body); @@ -109,7 +113,7 @@ public class MenuBar extends Widget implements PopupListener { this.vertical = vertical; - final Element outer = DOM.createDiv(); + outer = DOM.createDiv(); DOM.appendChild(outer, table); setElement(outer); @@ -324,6 +328,37 @@ public class MenuBar extends Widget implements PopupListener { return selectedItem; } + /** + * Gets the first item from the menu or null if no items. + * + * @since + * @return the first item from the menu or null if no items. + */ + public MenuItem getFirstItem() { + return items != null && items.size() > 0 ? items.get(0) : null; + } + + /** + * Gest the last item from the menu or null if no items. + * + * @since + * @return the last item from the menu or null if no items. + */ + public MenuItem getLastItem() { + return items != null && items.size() > 0 ? items.get(items.size() - 1) + : null; + } + + /** + * Gets the index of the selected item. + * + * @since + * @return the index of the selected item. + */ + public int getSelectedIndex() { + return items != null ? items.indexOf(getSelectedItem()) : -1; + } + @Override protected void onDetach() { // When the menu is detached, make sure to close all of its children. @@ -468,6 +503,7 @@ public class MenuBar extends Widget implements PopupListener { public void selectItem(MenuItem item) { if (item == selectedItem) { + scrollItemIntoView(item); return; } @@ -480,6 +516,74 @@ public class MenuBar extends Widget implements PopupListener { } selectedItem = item; + + scrollItemIntoView(item); + } + + /* + * Scroll the specified item into view. + */ + private void scrollItemIntoView(MenuItem item) { + if (item != null) { + item.getElement().scrollIntoView(); + } + } + + /** + * Scroll the selected item into view. + * + * @since + */ + public void scrollSelectionIntoView() { + scrollItemIntoView(selectedItem); + } + + /** + * Sets the menu scroll enabled or disabled. + * + * @since + * @param enabled + * the enabled state of the scroll. + */ + public void setScrollEnabled(boolean enabled) { + if (enabled) { + if (vertical) { + outer.getStyle().setOverflowY(Overflow.AUTO); + } else { + outer.getStyle().setOverflowX(Overflow.AUTO); + } + + } else { + if (vertical) { + outer.getStyle().clearOverflowY(); + } else { + outer.getStyle().clearOverflowX(); + } + } + } + + /** + * Gets whether the scroll is activate for this menu. + * + * @since + * @return true if the scroll is active, otherwise false. + */ + public boolean isScrollActive() { + // Element element = getElement(); + // return element.getOffsetHeight() > DOM.getChild(element, 0) + // .getOffsetHeight(); + int outerHeight = outer.getOffsetHeight(); + int tableHeight = table.getOffsetHeight(); + return outerHeight < tableHeight; + } + + /** + * Gets the preferred height of the menu. + * + * @since + */ + protected int getPreferredHeight() { + return table.getOffsetHeight(); } /** diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreen.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreen.java new file mode 100644 index 0000000000..044214cecf --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreen.java @@ -0,0 +1,76 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.combobox; + +import com.vaadin.data.Item; +import com.vaadin.server.ClassResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.ComboBox; + +/** + * Test UI for issue #11929 where ComboBox suggestion popup hides the ComboBox + * itself obscuring the text input field. + * + * @author Vaadin Ltd + */ +public class ComboBoxOnSmallScreen extends AbstractTestUI { + + private static final String PID = "captionPID"; + + @Override + protected void setup(VaadinRequest request) { + addComponents(createComboBox()); + } + + @Override + protected String getTestDescription() { + return "Combobox hides what you are typing on small screen"; + } + + @Override + protected Integer getTicketNumber() { + return 11929; + } + + private ComboBox createComboBox() { + ComboBox cb = new ComboBox(); + cb.addContainerProperty(PID, String.class, ""); + cb.setItemCaptionPropertyId(PID); + + Object selectId = null; + + for (int i = 1; i < 22; ++i) { + final String v = "Item #" + i; + Object itemId = cb.addItem(); + + if (i == 9) { + selectId = itemId; + } + + Item item = cb.getItem(itemId); + item.getItemProperty(PID).setValue(v); + int flagIndex = i % 3; + cb.setItemIcon(itemId, new ClassResource( + flagIndex == 0 ? "fi_small.png" : flagIndex == 1 ? "fi.gif" + : "se.gif")); + } + + cb.select(selectId); + + return cb; + } +} diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreenTest.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreenTest.java new file mode 100644 index 0000000000..f48f2bbdeb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxOnSmallScreenTest.java @@ -0,0 +1,84 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.combobox; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebDriver.Window; +import org.openqa.selenium.WebElement; + +import com.vaadin.client.ui.VFilterSelect; +import com.vaadin.testbench.elements.ComboBoxElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * ComboBox suggestion popup should not obscure the text input box. + * + * @author Vaadin Ltd + */ +public class ComboBoxOnSmallScreenTest extends MultiBrowserTest { + + private static final Dimension TARGETSIZE = new Dimension(600, 300); + private static final String POPUPCLASSNAME = VFilterSelect.CLASSNAME + + "-suggestpopup"; + + ComboBoxElement combobox; + WebElement popup; + + @Override + public void setup() throws Exception { + super.setup(); + + openTestURL(); + + getWindow().setSize(TARGETSIZE); + + combobox = $(ComboBoxElement.class).first(); + combobox.openPopup(); + + popup = findElement(By.className(POPUPCLASSNAME)); + } + + @Test + public void testSuggestionPopupOverlayPosition() { + final int popupTop = popup.getLocation().y; + final int popupBottom = popupTop + popup.getSize().getHeight(); + final int cbTop = combobox.getLocation().y; + final int cbBottom = cbTop + combobox.getSize().getHeight(); + + assertThat("Popup overlay overlaps with the textbox", + popupTop >= cbBottom || popupBottom <= cbTop, is(true)); + } + + @Test + public void testSuggestionPopupOverlaySize() { + final int popupTop = popup.getLocation().y; + final int popupBottom = popupTop + popup.getSize().getHeight(); + final int rootHeight = findElement(By.tagName("body")).getSize().height; + + assertThat("Popup overlay out of the screen", popupTop < 0 + || popupBottom > rootHeight, is(false)); + } + + private Window getWindow() { + return getDriver().manage().window(); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/combobox/fi.png b/uitest/src/com/vaadin/tests/components/combobox/fi.png new file mode 100644 index 0000000000..976a9663ce Binary files /dev/null and b/uitest/src/com/vaadin/tests/components/combobox/fi.png differ diff --git a/uitest/src/com/vaadin/tests/components/combobox/fi_small.png b/uitest/src/com/vaadin/tests/components/combobox/fi_small.png new file mode 100644 index 0000000000..2908973fa4 Binary files /dev/null and b/uitest/src/com/vaadin/tests/components/combobox/fi_small.png differ -- cgit v1.2.3 From 441371ac98b2a99822ca1639a6823b23be93c229 Mon Sep 17 00:00:00 2001 From: Markus Koivisto Date: Thu, 26 Jun 2014 16:35:42 +0300 Subject: Keyboard shift-selection now works as expected in VScrollTable. (#14094) Change-Id: I0dcd9f75cd30fe91c17ca0755241e73a37da79ec --- client/src/com/vaadin/client/ui/VScrollTable.java | 19 ++++---- .../table/SelectAllConstantViewport.java | 6 ++- .../table/SelectAllConstantViewportTest.java | 57 +++++++++++++++++++++- .../src/com/vaadin/tests/tb3/MultiBrowserTest.java | 10 +++- 4 files changed, 78 insertions(+), 14 deletions(-) (limited to 'client') diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 59645aa6d3..5e6207f53f 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1093,9 +1093,17 @@ public class VScrollTable extends FlowPanel implements HasWidgets, /* * The focus is no longer on a selected row. Move * focus to the selected row. (#10522) + * + * Don't do this for multiselect (#13341). + * + * Checking the selection mode here instead of in + * setRowFocus allows keyboard shift+downarrow + * selection to work as expected. */ + if (isSingleSelectMode()) { + setRowFocus(row); + } - setRowFocus(row); } } if (selected != row.isSelected()) { @@ -7248,14 +7256,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // Set new focused row focusedRow = row; - /* - * Don't scroll to the focused row when in multiselect mode. - * (#13341) - */ - - if (isSingleSelectMode()) { - ensureRowIsVisible(row); - } + ensureRowIsVisible(row); return true; } diff --git a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java index 5a406eac48..35ac63efe2 100644 --- a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java +++ b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewport.java @@ -65,7 +65,7 @@ public class SelectAllConstantViewport extends AbstractTestUIWithLog { table.addItem(new Object[] { new Integer(i) }, new Integer(i)); } - table.setCurrentPageFirstItemIndex(185); + table.setCurrentPageFirstItemIndex(178); final CssLayout layout = new CssLayout(); layout.addComponent(selectAllCheckbox); @@ -82,7 +82,9 @@ public class SelectAllConstantViewport extends AbstractTestUIWithLog { @Override protected String getTestDescription() { - return "The scroll position of a table with many items should remain constant if all items are selected."; + return "The scroll position of a table with many items should remain constant if all items are " + + "selected. The scroll position should change if the user uses the keyboard to select " + + "multiple lines with shift+arrowkeys."; } /* diff --git a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java index 0e7a7c08a4..257efef6a2 100644 --- a/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java +++ b/uitest/src/com/vaadin/tests/components/table/SelectAllConstantViewportTest.java @@ -15,14 +15,20 @@ */ package com.vaadin.tests.components.table; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; +import java.util.List; import org.junit.Test; +import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedCondition; import com.vaadin.testbench.By; @@ -33,7 +39,7 @@ import com.vaadin.tests.tb3.MultiBrowserTest; public class SelectAllConstantViewportTest extends MultiBrowserTest { @Test - public void testViewportUnchanged() throws IOException { + public void testViewportUnchangedwithMultiSel() throws IOException { openTestURL(); CheckBoxElement checkbox = $(CheckBoxElement.class).first(); @@ -51,11 +57,58 @@ public class SelectAllConstantViewportTest extends MultiBrowserTest { int rowLocation = row.getLocation().getY(); - // use click x,y with non-zero offset to actually toggle the checkbox. (#13763) + // use click x,y with non-zero offset to actually toggle the checkbox. + // (#13763) checkbox.click(5, 5); int newRowLocation = row.getLocation().getY(); assertThat(newRowLocation, is(rowLocation)); } + + @Test + public void testViewportChangedwithKeyboardSel() throws IOException { + openTestURL(); + + WebElement cell = $(TableElement.class).first().getCell(190, 0); + final WebElement scrollPositionDisplay = getDriver().findElement( + By.className("v-table-scrollposition")); + waitUntilNot(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return scrollPositionDisplay.isDisplayed(); + } + }, 10); + + int rowLocation = cell.getLocation().getY(); + + // select downwards with shift (#14094) + + cell.click(); + + final WebElement row = getDriver().findElement( + By.className("v-selected")); + + assertThat(row.getAttribute("class"), containsString("selected")); + + // for some reason phantomJS does not support keyboard actions + + Actions action = new Actions(getDriver()); + action.keyDown(Keys.SHIFT) + .sendKeys(Keys.ARROW_DOWN, Keys.ARROW_DOWN, Keys.ARROW_DOWN, + Keys.ARROW_DOWN, Keys.ARROW_DOWN, Keys.ARROW_DOWN, + Keys.ARROW_DOWN).keyUp(Keys.SHIFT).build().perform(); + + int newRowLocation = cell.getLocation().getY(); + + assertThat(newRowLocation, is(not(rowLocation))); + + } + + @Override + public List getBrowsersToTest() { + // phantomJS does not support keyboard actions + return getBrowsersExcludingPhantomJS(); + } } diff --git a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java index ccbb6ca872..ffa0b83dc2 100644 --- a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java +++ b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java @@ -41,7 +41,8 @@ import org.openqa.selenium.remote.DesiredCapabilities; public abstract class MultiBrowserTest extends PrivateTB3Configuration { protected List getBrowsersExcludingIE() { - List browsers = new ArrayList(getAllBrowsers()); + List browsers = new ArrayList( + getAllBrowsers()); browsers.remove(Browser.IE8.getDesiredCapabilities()); browsers.remove(Browser.IE9.getDesiredCapabilities()); browsers.remove(Browser.IE10.getDesiredCapabilities()); @@ -50,6 +51,13 @@ public abstract class MultiBrowserTest extends PrivateTB3Configuration { return browsers; } + protected List getBrowsersExcludingPhantomJS() { + List browsers = new ArrayList( + getAllBrowsers()); + browsers.remove(Browser.PHANTOMJS.getDesiredCapabilities()); + return browsers; + } + public enum Browser { FIREFOX(BrowserUtil.firefox(24)), CHROME(BrowserUtil.chrome(33)), SAFARI( BrowserUtil.safari(7)), IE8(BrowserUtil.ie(8)), IE9(BrowserUtil -- cgit v1.2.3 From e7632140cfe81062f1f81408c643e722661e60b5 Mon Sep 17 00:00:00 2001 From: Juuso Valli Date: Thu, 24 Jul 2014 10:54:42 +0300 Subject: Fix VWindow Vai-Aria roles for alertdialogs (#14289) Change-Id: Ie33ef684f2177fe1807f95bf234031cc3a44f317 --- client/src/com/vaadin/client/ui/VWindow.java | 8 +- .../tests/accessibility/WindowWaiAriaRoles.java | 107 +++++++++++++++++++++ .../accessibility/WindowWaiAriaRolesTest.java | 54 +++++++++++ 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRoles.java create mode 100644 uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRolesTest.java (limited to 'client') diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index 83a0001ad8..495e230156 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.java @@ -233,7 +233,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /* * Stores the element that has focus in the application UI when the * window is opened, so it can be restored when the window closes. - * + * * This is currently implemented for the case when one non-modal window * can be open at the same time, and the focus is not changed while the * window is open. @@ -253,7 +253,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /* * Restores the previously stored focused element. - * + * * When the focus was changed outside the window while the window was * open, the originally stored element is restored. */ @@ -640,7 +640,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * correctly if clicking on the "close" button in the window header but * closing the window from a button for example in the window will fail. * Symptom described in #10776 - * + * * The problematic part is that for the focus to be returned correctly * an input element needs to be focused in the root panel. Focusing some * other element apparently won't work. @@ -1411,7 +1411,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * WAI-ARIA role to set for the window */ public void setWaiAriaRole(WindowRole role) { - if ("alertdialog".equals(role)) { + if (role == WindowRole.ALERTDIALOG) { Roles.getAlertdialogRole().set(getElement()); } else { Roles.getDialogRole().set(getElement()); diff --git a/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRoles.java b/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRoles.java new file mode 100644 index 0000000000..2ab6be25ac --- /dev/null +++ b/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRoles.java @@ -0,0 +1,107 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.accessibility; + +import java.util.Stack; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.window.WindowRole; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Window; + +/** + * UI to test if subwindows get the correct assistive roles. + * + * @author Vaadin Ltd + */ +public class WindowWaiAriaRoles extends AbstractTestUI { + Stack windows = new Stack(); + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + Button closeButton = new Button("Close windows"); + closeButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + while (!windows.isEmpty()) { + Window window = windows.pop(); + removeWindow(window); + } + } + + }); + + Button regularButton = new Button("Regular"); + regularButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + Window regularWindow = new Window("Regular window"); + openWindow(regularWindow); + } + }); + + Button alertButton = new Button("Alert"); + alertButton.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + Window alertWindow = new Window("Alert window"); + alertWindow.setAssistiveRole(WindowRole.ALERTDIALOG); + openWindow(alertWindow); + } + }); + addComponent(closeButton); + addComponent(regularButton); + addComponent(alertButton); + } + + void openWindow(Window window) { + windows.push(window); + window.center(); + addWindow(window); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "The alert window should have the role 'alertdialog' and the regular window should have the role 'dialog'"; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 14289; + } + +} diff --git a/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRolesTest.java b/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRolesTest.java new file mode 100644 index 0000000000..e1d0452708 --- /dev/null +++ b/uitest/src/com/vaadin/tests/accessibility/WindowWaiAriaRolesTest.java @@ -0,0 +1,54 @@ +/* + * 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 + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.accessibility; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.WindowElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test to see if regular and alert windows get the correct wai-aria roles + * + * @author Vaadin Ltd + */ +public class WindowWaiAriaRolesTest extends MultiBrowserTest { + + @Test + public void testRegularWindowRole() { + openTestURL(); + + $(ButtonElement.class).caption("Regular").first().click(); + String role = getWindowRole(); + Assert.assertTrue("Dialog has incorrect role '" + role + + "', expected 'dialog'", "dialog".equals(role)); + } + + @Test + public void testAlertWindowRole() { + openTestURL(); + $(ButtonElement.class).caption("Alert").first().click(); + String role = getWindowRole(); + Assert.assertTrue("Dialog has incorrect role '" + role + + "', expected 'alertdialog'", "alertdialog".equals(role)); + } + + public String getWindowRole() { + return $(WindowElement.class).first().getAttribute("role"); + } +} -- cgit v1.2.3 From 236293303bff740619a95131d5360bdbfe021c95 Mon Sep 17 00:00:00 2001 From: Bogdan Udrescu Date: Wed, 2 Jul 2014 18:29:56 +0300 Subject: TextArea size get reset when css resize is set (#14080) Listen to MouseUp event on the