--- /dev/null
+/*
+ * 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);
+ }
+ };
+
+ }
+
+}
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)
}
@Override
- public VCustomComponent getWidget() {
- return (VCustomComponent) super.getWidget();
+ public CustomFieldState getState() {
+ return (CustomFieldState) super.getState();
+ }
+
+ @Override
+ public VCustomField getWidget() {
+ return (VCustomField) super.getWidget();
}
@Override
// 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) {
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
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);
+ }
+ }
+
}
--- /dev/null
+/*
+ * 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;
+
+}
Button addCountryButton = new Button("New");
fieldLayout.addComponent(addCountryButton);
+ setFocusDelegate(cityComboBox);
+
return fieldLayout;
}
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 {
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());
+ }
}