diff options
author | Artur Signell <artur@vaadin.com> | 2013-03-06 14:55:15 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-03-12 12:16:33 +0000 |
commit | 36a201f22f66989bf7a78ce8afce1ee64aaa0a22 (patch) | |
tree | 2e0957f0d65734e5bfe2e9242a196b387a44d21c | |
parent | 6922bc5b49c5551b289a5025ccd5901e2ac3aafc (diff) | |
download | vaadin-framework-36a201f22f66989bf7a78ce8afce1ee64aaa0a22.tar.gz vaadin-framework-36a201f22f66989bf7a78ce8afce1ee64aaa0a22.zip |
Fixed focus and tab index handling for UI (#11129)
Change-Id: I80377792ade11946337e2900a7ea84209ae1d060
-rw-r--r-- | client/src/com/vaadin/client/ui/AbstractComponentConnector.java | 13 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/FocusUtil.java | 94 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/VUI.java | 25 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/ui/UIConnector.java | 8 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/UI.java | 17 | ||||
-rw-r--r-- | shared/src/com/vaadin/shared/ui/ui/UIState.java | 6 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/components/ui/UITabIndex.html | 57 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/components/ui/UITabIndex.java | 51 |
8 files changed, 258 insertions, 13 deletions
diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index 694db6f02c..74cb85c297 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -137,10 +137,15 @@ public abstract class AbstractComponentConnector extends AbstractConnector * implementation). */ Profiler.enter("AbstractComponentConnector.onStateChanged update tab index"); - if (getState() instanceof TabIndexState - && getWidget() instanceof Focusable) { - ((Focusable) getWidget()) - .setTabIndex(((TabIndexState) getState()).tabIndex); + if (getState() instanceof TabIndexState) { + if (getWidget() instanceof Focusable) { + ((Focusable) getWidget()) + .setTabIndex(((TabIndexState) getState()).tabIndex); + } else { + VConsole.error("Tab index received for " + + Util.getSimpleName(getWidget()) + + " which does not implement Focusable"); + } } Profiler.leave("AbstractComponentConnector.onStateChanged update tab index"); diff --git a/client/src/com/vaadin/client/ui/FocusUtil.java b/client/src/com/vaadin/client/ui/FocusUtil.java new file mode 100644 index 0000000000..7c0fea5f6d --- /dev/null +++ b/client/src/com/vaadin/client/ui/FocusUtil.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2013 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.client.ui; + +import com.google.gwt.user.client.ui.Focusable; +import com.google.gwt.user.client.ui.Widget; + +/** + * A helper class used to make it easier for {@link Widget}s to implement + * {@link Focusable}. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.3 + * + */ +public class FocusUtil { + + /** + * Sets the access key property + * + * @param focusable + * The widget for which we want to set the access key. + * @param key + * The access key to set + */ + public static void setAccessKey(Widget focusable, char key) { + assert (focusable != null && focusable.getElement() != null) : "Can't setAccessKey for a widget without an element"; + focusable.getElement().setPropertyString("accessKey", "" + key); + } + + /** + * Explicitly focus/unfocus the given widget. Only one widget can have focus + * at a time, and the widget that does will receive all keyboard events. + * + * @param focusable + * the widget to focus/unfocus + * @param focused + * whether this widget should take focus or release it + */ + public static void setFocus(Widget focusable, boolean focus) { + assert (focusable != null && focusable.getElement() != null) : "Can't setFocus for a widget without an element"; + + if (focus) { + focusable.getElement().focus(); + } else { + focusable.getElement().blur(); + } + } + + /** + * Sets the widget's position in the tab index. If more than one widget has + * the same tab index, each such widget will receive focus in an arbitrary + * order. Setting the tab index to <code>-1</code> will cause the widget to + * be removed from the tab order. + * + * @param focusable + * The widget + * @param tabIndex + * the widget's tab index + */ + public static void setTabIndex(Widget focusable, int tabIndex) { + assert (focusable != null && focusable.getElement() != null) : "Can't setTabIndex for a widget without an element"; + + focusable.getElement().setTabIndex(tabIndex); + } + + /** + * Gets the widget's position in the tab index. + * + * @param focusable + * The widget + * + * @return the widget's tab index + */ + public static int getTabIndex(Widget focusable) { + assert (focusable != null && focusable.getElement() != null) : "Can't getTabIndex for a widget without an element"; + + return focusable.getElement().getTabIndex(); + } +} diff --git a/client/src/com/vaadin/client/ui/VUI.java b/client/src/com/vaadin/client/ui/VUI.java index b627d4a2a9..b07593896f 100644 --- a/client/src/com/vaadin/client/ui/VUI.java +++ b/client/src/com/vaadin/client/ui/VUI.java @@ -53,7 +53,8 @@ import com.vaadin.shared.ui.ui.UIConstants; */ public class VUI extends SimplePanel implements ResizeHandler, Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable, - HasResizeHandlers, HasScrollHandlers { + com.google.gwt.user.client.ui.Focusable, HasResizeHandlers, + HasScrollHandlers { private static int MONITOR_PARENT_TIMER_INTERVAL = 1000; @@ -437,7 +438,7 @@ public class VUI extends SimplePanel implements ResizeHandler, @Override public void focus() { - getElement().focus(); + setFocus(true); } /** @@ -462,4 +463,24 @@ public class VUI extends SimplePanel implements ResizeHandler, return addHandler(scrollHandler, ScrollEvent.getType()); } + @Override + public int getTabIndex() { + return FocusUtil.getTabIndex(this); + } + + @Override + public void setAccessKey(char key) { + FocusUtil.setAccessKey(this, key); + } + + @Override + public void setFocus(boolean focused) { + FocusUtil.setFocus(this, focused); + } + + @Override + public void setTabIndex(int index) { + FocusUtil.setTabIndex(this, index); + } + } diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index 0fb7439587..ac441fc625 100644 --- a/client/src/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java @@ -329,10 +329,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector DOM.sinkEvents(getWidget().getElement(), Event.ONKEYDOWN | Event.ONSCROLL); - // iview is focused when created so element needs tabIndex - // 1 due 0 is at the end of natural tabbing order - DOM.setElementProperty(getWidget().getElement(), "tabIndex", "1"); - RootPanel root = RootPanel.get(rootPanelId); // Remove the v-app-loading or any splash screen added inside the div by @@ -347,6 +343,10 @@ public class UIConnector extends AbstractSingleComponentContainerConnector root.add(getWidget()); + // Set default tab index before focus call. State change handler + // will update this later if needed. + getWidget().setTabIndex(1); + if (applicationConnection.getConfiguration().isStandalone()) { // set focus to iview element by default to listen possible keyboard // shortcuts. For embedded applications this is unacceptable as we diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index d12c8d89c9..796d1f08ea 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -78,7 +78,7 @@ import com.vaadin.util.CurrentInstance; * @since 7.0 */ public abstract class UI extends AbstractSingleComponentContainer implements - Action.Container, Action.Notifier, LegacyComponent { + Action.Container, Action.Notifier, LegacyComponent, Focusable { /** * The application to which this UI belongs @@ -184,6 +184,11 @@ public abstract class UI extends AbstractSingleComponentContainer implements } @Override + protected UIState getState(boolean markAsDirty) { + return (UIState) super.getState(markAsDirty); + } + + @Override public Class<? extends UIState> getStateType() { // This is a workaround for a problem with creating the correct state // object during build @@ -1039,4 +1044,14 @@ public abstract class UI extends AbstractSingleComponentContainer implements } super.setContent(content); } + + @Override + public void setTabIndex(int tabIndex) { + getState().tabIndex = tabIndex; + } + + @Override + public int getTabIndex() { + return getState(false).tabIndex; + } } diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index aa862ae31a..9abaf47f4b 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -15,10 +15,12 @@ */ package com.vaadin.shared.ui.ui; -import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.ui.TabIndexState; -public class UIState extends AbstractComponentState { +public class UIState extends TabIndexState { { primaryStyleName = "v-ui"; + // Default is 1 for legacy reasons + tabIndex = 1; } }
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/ui/UITabIndex.html b/uitest/src/com/vaadin/tests/components/ui/UITabIndex.html new file mode 100644 index 0000000000..fa083f1489 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UITabIndex.html @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.ui.UITabIndex?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::@tabIndex</td> + <td>1</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::@tabIndex</td> + <td>-1</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::@tabIndex</td> + <td>0</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsuiUITabIndex::@tabIndex</td> + <td>1</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/ui/UITabIndex.java b/uitest/src/com/vaadin/tests/components/ui/UITabIndex.java new file mode 100644 index 0000000000..083eaf3f7d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UITabIndex.java @@ -0,0 +1,51 @@ +package com.vaadin.tests.components.ui; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; + +public class UITabIndex extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Button b; + + b = new Button("Set tabIndex to -1"); + b.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + setTabIndex(-1); + } + }); + addComponent(b); + b = new Button("Set tabIndex to 0"); + b.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + setTabIndex(0); + } + }); + addComponent(b); + b = new Button("Set tabIndex to 1"); + b.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + setTabIndex(1); + } + }); + addComponent(b); + } + + @Override + protected String getTestDescription() { + return "Tests tab index handling for UI"; + } + + @Override + protected Integer getTicketNumber() { + return 11129; + } + +} |