OSDN Git Service

Add Request Focus to textfields
authorTor Norbye <tnorbye@google.com>
Mon, 2 May 2011 22:57:02 +0000 (15:57 -0700)
committerTor Norbye <tnorbye@google.com>
Mon, 2 May 2011 22:58:34 +0000 (15:58 -0700)
This changeset adds a couple of focus related changes:

1) When the first text field is added into a layout, it is
  automatically requesting focus

2) There is a new context menu item available on textfields to request
   focus. When invoked on a text field, it will both add
   <requestFocus> to itself and remove it from any other text fields
   that have set it in the layout. When invoked on a text field which
   already has focus, it will offer to clear the focus.

3) The Advanced section of the palette also contains the
   <requestFocus> tag.

Change-Id: I92982b6dfc17315b3d513c304f258b8901a007a4

eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml

diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png
new file mode 100644 (file)
index 0000000..205a032
Binary files /dev/null and b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java
new file mode 100644 (file)
index 0000000..dfe9d6f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.common.layout;
+
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS;
+
+import com.android.ide.common.api.IMenuCallback;
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.INodeHandler;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+import com.android.ide.common.api.MenuAction;
+
+import java.util.List;
+
+/**
+ * An {@link IViewRule} for android.widget.EditText.
+ */
+public class EditTextRule extends BaseViewRule {
+
+    @Override
+    public void onCreate(INode node, INode parent, InsertType insertType) {
+        super.onCreate(node, parent, insertType);
+
+        if (parent != null) {
+            INode focus = findFocus(findRoot(parent));
+            if (focus == null) {
+                // Add <requestFocus>
+                node.appendChild(REQUEST_FOCUS);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Adds a "Request Focus" menu item.
+     */
+    @Override
+    public List<MenuAction> getContextMenu(final INode selectedNode) {
+        final boolean hasFocus = hasFocus(selectedNode);
+        final String label = hasFocus ? "Clear Focus" : "Request Focus";
+
+        IMenuCallback onChange = new IMenuCallback() {
+            public void action(MenuAction menuAction, String valueId, Boolean newValue) {
+                selectedNode.editXml(label, new INodeHandler() {
+                    public void handle(INode node) {
+                        INode focus = findFocus(findRoot(node));
+                        if (focus != null && focus.getParent() != null) {
+                            focus.getParent().removeChild(focus);
+                        }
+                        if (!hasFocus) {
+                            node.appendChild(REQUEST_FOCUS);
+                        }
+                    }
+                });
+            }
+        };
+
+        return concatenate(super.getContextMenu(selectedNode),
+            new MenuAction.Action("_setfocus", label, null, onChange));  //$NON-NLS-1$
+    }
+
+    /** Returns true if the given node currently has focus */
+    private static boolean hasFocus(INode node) {
+        INode focus = findFocus(node);
+        if (focus != null) {
+            return focus.getParent() == node;
+        }
+
+        return false;
+    }
+
+    /** Returns the root/top level node in the view hierarchy that contains the given node */
+    private static INode findRoot(INode node) {
+        // First find the parent
+        INode root = node;
+        while (root != null) {
+            INode parent = root.getParent();
+            if (parent == null) {
+                break;
+            } else {
+                root = parent;
+            }
+        }
+
+        return root;
+    }
+
+    /** Finds the focus node (not the node containing focus, but the actual request focus node
+     * under a given node */
+    private static INode findFocus(INode node) {
+        if (node.getFqcn().equals(REQUEST_FOCUS)) {
+            return node;
+        }
+
+        for (INode child : node.getChildren()) {
+            INode focus = findFocus(child);
+            if (focus != null) {
+                return focus;
+            }
+        }
+        return null;
+    }
+
+}
index f88e7ab..bb04498 100644 (file)
@@ -29,10 +29,10 @@ import com.android.sdklib.SdkConstants;
  * </ul>
  */
 public class LayoutConstants {
-    /** The element name in a <code>&lt;view class="..."&gt;</code> element. */
+    /** The element name in a {@code <view class="...">} element. */
     public static final String VIEW = "view";                           //$NON-NLS-1$
 
-    /** The attribute name in a <code>&lt;view class="..."&gt;</code> element. */
+    /** The attribute name in a {@code <view class="...">} element. */
     public static final String ATTR_CLASS = "class";                    //$NON-NLS-1$
     public static final String ATTR_ON_CLICK = "onClick";               //$NON-NLS-1$
 
index 0384ad5..c7bc5a1 100644 (file)
@@ -32,6 +32,7 @@ import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
 import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS;
 
 import com.android.ide.common.api.IAttributeInfo.Format;
 import com.android.ide.common.resources.platform.AttributeInfo;
@@ -699,6 +700,12 @@ public final class DescriptorsUtils {
         // if this ui_node is a layout and we're adding it to a document, use match_parent for
         // both W/H. Otherwise default to wrap_layout.
         ElementDescriptor descriptor = node.getDescriptor();
+
+        if (descriptor.getXmlLocalName().equals(REQUEST_FOCUS)) {
+            // Don't add ids etc to <requestFocus>
+            return;
+        }
+
         boolean fill = descriptor.hasChildren() &&
                        node.getUiParent() instanceof UiDocumentNode;
         node.setAttributeValue(
index b61c069..4613492 100644 (file)
@@ -42,20 +42,27 @@ import java.util.Map.Entry;
 public final class LayoutDescriptors implements IDescriptorProvider {
 
     /**
-     * The XML name of the special &lt;include&gt; layout tag.
+     * The XML name of the special {@code <include>} layout tag.
      * A synthetic element with that name is created as part of the view descriptors list
      * returned by {@link #getViewDescriptors()}.
      */
     public static final String VIEW_INCLUDE = "include";      //$NON-NLS-1$
 
     /**
-     * The XML name of the special &lt;merge&gt; layout tag.
+     * The XML name of the special {@code <merge>} layout tag.
      * A synthetic element with that name is created as part of the view descriptors list
      * returned by {@link #getViewDescriptors()}.
      */
     public static final String VIEW_MERGE = "merge";          //$NON-NLS-1$
 
     /**
+     * The XML name of the special {@code <requestFocus>} layout tag.
+     * A synthetic element with that name is created as part of the view descriptors list
+     * returned by {@link #getViewDescriptors()}.
+     */
+    public static final String REQUEST_FOCUS = "requestFocus";//$NON-NLS-1$
+
+    /**
      * The attribute name of the include tag's url naming the resource to be inserted
      * <p>
      * <b>NOTE</b>: The layout attribute is NOT in the Android namespace!
@@ -174,6 +181,10 @@ public final class LayoutDescriptors implements IDescriptorProvider {
 
         fixSuperClasses(infoDescMap);
 
+        ViewElementDescriptor requestFocus = createRequestFocus();
+        newViews.add(requestFocus);
+        newDescriptors.add(requestFocus);
+
         // The <merge> tag can only be a root tag, so it is added at the end.
         // It gets everything else as children but it is not made a child itself.
         ViewElementDescriptor mergeTag = createMerge(newLayouts);
@@ -342,7 +353,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
     }
 
     /**
-     * Creates and return a new <merge> descriptor.
+     * Creates and return a new {@code <merge>} descriptor.
      * @param knownLayouts  A list of all known layout view descriptors, used to find the
      *   FrameLayout descriptor and extract its layout attributes.
      */
@@ -368,6 +379,27 @@ public final class LayoutDescriptors implements IDescriptorProvider {
     }
 
     /**
+     * Creates and return a new {@code <requestFocus>} descriptor.
+     * @param knownLayouts  A list of all known layout view descriptors, used to find the
+     *   FrameLayout descriptor and extract its layout attributes.
+     */
+    private ViewElementDescriptor createRequestFocus() {
+        String xml_name = REQUEST_FOCUS;
+
+        // Create the include descriptor
+        return new ViewElementDescriptor(
+                xml_name,  // xml_name
+                xml_name, // ui_name
+                xml_name, // "class name"; the GLE only treats this as an element tag
+                "Requests focus for the parent element or one of its descendants", // tooltip
+                null,  // sdk_url
+                null,  // attributes
+                null,  // layout attributes
+                null,  // children
+                false  /* mandatory */);
+    }
+
+    /**
      * Finds the descriptor and retrieves all its layout attributes.
      */
     private AttributeDescriptor[] findViewLayoutAttributes(
index 0870f69..442b76c 100644 (file)
     <category
         name="Advanced">
         <view
+            class="requestFocus"
+            render="skip" />
+        <view
             class="android.view.View"
             render="skip" />
         <view