diff options
author | Artur Signell <artur@vaadin.com> | 2016-09-30 23:30:42 +0300 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2016-10-26 22:11:19 +0300 |
commit | 44603b49f80b4191eca8f82a8cc17243820aa2c4 (patch) | |
tree | 8bbf711428687c8903fb1528598797d88f872310 | |
parent | ece3ac4f2fc3db486796df8c70e9381349a8c091 (diff) | |
download | vaadin-framework-44603b49f80b4191eca8f82a8cc17243820aa2c4.tar.gz vaadin-framework-44603b49f80b4191eca8f82a8cc17243820aa2c4.zip |
Allow defining a focus delegate component for CustomField (#20336)
Change-Id: I1160e7a384b1816204eb7f4b0f52f83ed9e230c0
6 files changed, 205 insertions, 4 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VCustomField.java b/client/src/main/java/com/vaadin/client/ui/VCustomField.java new file mode 100644 index 0000000000..5799e5fa27 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VCustomField.java @@ -0,0 +1,59 @@ +/* + * 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.client.ui; + +import com.vaadin.client.Focusable; + +public class VCustomField extends VCustomComponent implements Focusable { + + private Focusable focusDelegate; + + @Override + public void focus() { + if (focusDelegate != null) { + focusDelegate.focus(); + } + } + + /** + * Sets the focusable widget to focus instead of this custom field. + * + * @param focusDelegate + * the widget to delegate focus to + */ + public void setFocusDelegate(Focusable focusDelegate) { + this.focusDelegate = focusDelegate; + + } + + /** + * Sets the focusable widget to focus instead of this custom field. + * + * @param focusDelegate + * the widget to delegate focus to + */ + public void setFocusDelegate( + final com.google.gwt.user.client.ui.Focusable focusDelegate) { + this.focusDelegate = new Focusable() { + @Override + public void focus() { + focusDelegate.setFocus(true); + } + }; + + } + +} diff --git a/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java index 7260599fe6..0b5a30149e 100644 --- a/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java @@ -17,16 +17,20 @@ package com.vaadin.client.ui.customfield; import java.util.Collections; import java.util.List; +import java.util.logging.Logger; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler; +import com.vaadin.client.Focusable; import com.vaadin.client.HasComponentsConnector; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; -import com.vaadin.client.ui.VCustomComponent; +import com.vaadin.client.ui.VCustomField; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.customfield.CustomFieldState; import com.vaadin.ui.CustomField; @Connect(value = CustomField.class) @@ -43,8 +47,13 @@ public class CustomFieldConnector extends AbstractFieldConnector } @Override - public VCustomComponent getWidget() { - return (VCustomComponent) super.getWidget(); + public CustomFieldState getState() { + return (CustomFieldState) super.getState(); + } + + @Override + public VCustomField getWidget() { + return (VCustomField) super.getWidget(); } @Override @@ -53,6 +62,32 @@ public class CustomFieldConnector extends AbstractFieldConnector } @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + if (getState().focusDelegate != null) { + Widget widget = ((ComponentConnector) getState().focusDelegate) + .getWidget(); + if (widget instanceof Focusable) { + getWidget().setFocusDelegate((Focusable) widget); + } else if (widget instanceof com.google.gwt.user.client.ui.Focusable) { + getWidget().setFocusDelegate( + (com.google.gwt.user.client.ui.Focusable) widget); + } else { + getLogger().warning( + "The given focus delegate does not implement Focusable: " + + widget.getClass().getName()); + } + } else { + getWidget().setFocusDelegate((Focusable) null); + } + + } + + private static Logger getLogger() { + return Logger.getLogger(CustomFieldConnector.class.getName()); + } + + @Override public void onConnectorHierarchyChange( ConnectorHierarchyChangeEvent event) { // We always have 1 child, unless the child is hidden diff --git a/server/src/main/java/com/vaadin/ui/CustomField.java b/server/src/main/java/com/vaadin/ui/CustomField.java index df8f9180c4..04c00aaf47 100644 --- a/server/src/main/java/com/vaadin/ui/CustomField.java +++ b/server/src/main/java/com/vaadin/ui/CustomField.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.Iterator; import com.vaadin.data.Property; +import com.vaadin.shared.ui.customfield.CustomFieldState; /** * A {@link Field} whose UI content can be constructed by the user, enabling the @@ -150,4 +151,64 @@ public abstract class CustomField<T> extends AbstractField<T> public Iterator<Component> iterator() { return new ComponentIterator(); } + + @Override + protected CustomFieldState getState() { + return (CustomFieldState) super.getState(); + } + + @Override + protected CustomFieldState getState(boolean markAsDirty) { + return (CustomFieldState) super.getState(markAsDirty); + } + + /** + * Sets the component to which all methods from the {@link Focusable} + * interface should be delegated. + * <p> + * Set this to a wrapped field to include that field in the tabbing order, + * to make it receive focus when {@link #focus()} is called and to make it + * be correctly focused when used as a Grid editor component. + * <p> + * By default, {@link Focusable} events are handled by the super class and + * ultimately ignored. + * + * @param focusDelegate + * the focusable component to which focus events are redirected + */ + public void setFocusDelegate(Focusable focusDelegate) { + getState().focusDelegate = focusDelegate; + } + + private Focusable getFocusable() { + return (Focusable) getState(false).focusDelegate; + } + + @Override + public void focus() { + if (getFocusable() != null) { + getFocusable().focus(); + } else { + super.focus(); + } + } + + @Override + public int getTabIndex() { + if (getFocusable() != null) { + return getFocusable().getTabIndex(); + } else { + return super.getTabIndex(); + } + } + + @Override + public void setTabIndex(int tabIndex) { + if (getFocusable() != null) { + getFocusable().setTabIndex(tabIndex); + } else { + super.setTabIndex(tabIndex); + } + } + } diff --git a/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java b/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java new file mode 100644 index 0000000000..3f9640c342 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java @@ -0,0 +1,29 @@ +/* + * 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.shared.ui.customfield; + +import com.vaadin.shared.AbstractFieldState; +import com.vaadin.shared.Connector; + +public class CustomFieldState extends AbstractFieldState { + + /** + * The component which should receive focus events instead of the custom + * field wrapper. + */ + public Connector focusDelegate; + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java index b1c6ee57c1..978f582478 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java @@ -73,6 +73,8 @@ public class GridEditorCustomField extends AbstractTestUIWithLog { Button addCountryButton = new Button("New"); fieldLayout.addComponent(addCountryButton); + setFocusDelegate(cityComboBox); + return fieldLayout; } diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java index 2acc554154..9e276f04b8 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java @@ -17,13 +17,14 @@ package com.vaadin.tests.components.grid; import org.junit.Assert; import org.junit.Test; +import org.openqa.selenium.Keys; import com.vaadin.testbench.TestBenchElement; -import com.vaadin.testbench.elements.ComboBoxElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elements.GridElement.GridEditorElement; import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.newelements.ComboBoxElement; @TestCategory("grid") public class GridEditorCustomFieldTest extends MultiBrowserTest { @@ -43,4 +44,18 @@ public class GridEditorCustomFieldTest extends MultiBrowserTest { Assert.assertEquals("Oslo", grid.getCell(0, 2).getText()); } + + @Test + public void tabReachesCustomField() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + grid.getCell(0, 1).doubleClick(); + GridEditorElement editor = grid.getEditor(); + editor.getField(0).sendKeys(Keys.TAB, Keys.TAB); + + ComboBoxElement comboBoxInCustomField = editor.getField(2) + .$(ComboBoxElement.class).first(); + assertElementsEquals(comboBoxInCustomField.getInputField(), + getActiveElement()); + } } |