]> source.dussan.org Git - vaadin-framework.git/commitdiff
Allow defining a focus delegate component for CustomField (#20336)
authorArtur Signell <artur@vaadin.com>
Fri, 30 Sep 2016 20:30:42 +0000 (23:30 +0300)
committerArtur Signell <artur@vaadin.com>
Wed, 26 Oct 2016 19:11:19 +0000 (22:11 +0300)
Change-Id: I1160e7a384b1816204eb7f4b0f52f83ed9e230c0

client/src/main/java/com/vaadin/client/ui/VCustomField.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/customfield/CustomFieldConnector.java
server/src/main/java/com/vaadin/ui/CustomField.java
shared/src/main/java/com/vaadin/shared/ui/customfield/CustomFieldState.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java
uitest/src/test/java/com/vaadin/tests/components/grid/GridEditorCustomFieldTest.java

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 (file)
index 0000000..5799e5f
--- /dev/null
@@ -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);
+            }
+        };
+
+    }
+
+}
index 7260599fe6dd67dce2652f896ccd644dcee6df6c..0b5a30149e8d27af5c7a8aabf5b87e43fa435965 100644 (file)
@@ -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
@@ -52,6 +61,32 @@ public class CustomFieldConnector extends AbstractFieldConnector
         // NOP, custom field does not render the caption of its content
     }
 
+    @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) {
index df8f9180c4266070370db1d9cbcf340731445d4a..04c00aaf477ff3da08fb3db995170f3f412cc2cc 100644 (file)
@@ -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 (file)
index 0000000..3f9640c
--- /dev/null
@@ -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;
+
+}
index b1c6ee57c1591fa3e077a8cfe1f4915d0632af07..978f5824788633cb9ccbb426ce943ee421822c68 100644 (file)
@@ -73,6 +73,8 @@ public class GridEditorCustomField extends AbstractTestUIWithLog {
             Button addCountryButton = new Button("New");
             fieldLayout.addComponent(addCountryButton);
 
+            setFocusDelegate(cityComboBox);
+
             return fieldLayout;
         }
 
index 2acc5541546a43d253d4342dd4378a194cf8bd7d..9e276f04b807a9c16e44bdf934118e753e6e8a87 100644 (file)
@@ -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());
+    }
 }