Home | History | Annotate | Download | only in layout
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.common.layout;
     18 
     19 import static com.android.SdkConstants.ANDROID_URI;
     20 import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
     21 import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
     22 import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
     23 
     24 import com.android.annotations.NonNull;
     25 import com.android.annotations.Nullable;
     26 import com.android.ide.common.api.DrawingStyle;
     27 import com.android.ide.common.api.DropFeedback;
     28 import com.android.ide.common.api.IDragElement;
     29 import com.android.ide.common.api.IFeedbackPainter;
     30 import com.android.ide.common.api.IGraphics;
     31 import com.android.ide.common.api.INode;
     32 import com.android.ide.common.api.INodeHandler;
     33 import com.android.ide.common.api.IViewMetadata;
     34 import com.android.ide.common.api.IViewMetadata.FillPreference;
     35 import com.android.ide.common.api.IViewRule;
     36 import com.android.ide.common.api.InsertType;
     37 import com.android.ide.common.api.Point;
     38 import com.android.ide.common.api.Rect;
     39 import com.android.ide.common.api.RuleAction;
     40 import com.android.utils.Pair;
     41 
     42 import java.util.List;
     43 import java.util.Map;
     44 
     45 /**
     46  * An {@link IViewRule} for android.widget.FrameLayout and all its derived
     47  * classes.
     48  */
     49 public class FrameLayoutRule extends BaseLayoutRule {
     50 
     51     // ==== Drag'n'drop support ====
     52     // The FrameLayout accepts any drag'n'drop anywhere on its surface.
     53 
     54     @Override
     55     public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
     56             final @Nullable IDragElement[] elements) {
     57         if (elements.length == 0) {
     58             return null;
     59         }
     60 
     61         return new DropFeedback(null, new IFeedbackPainter() {
     62             @Override
     63             public void paint(@NonNull IGraphics gc, @NonNull INode node,
     64                     @NonNull DropFeedback feedback) {
     65                 drawFeedback(gc, node, elements, feedback);
     66             }
     67         });
     68     }
     69 
     70     protected void drawFeedback(
     71             IGraphics gc,
     72             INode targetNode,
     73             IDragElement[] elements,
     74             DropFeedback feedback) {
     75         Rect b = targetNode.getBounds();
     76         if (!b.isValid()) {
     77             return;
     78         }
     79 
     80         gc.useStyle(DrawingStyle.DROP_RECIPIENT);
     81         gc.drawRect(b);
     82 
     83         // Get the drop point
     84         Point p = (Point) feedback.userData;
     85 
     86         if (p == null) {
     87             return;
     88         }
     89 
     90         Rect be = elements[0].getBounds();
     91 
     92         gc.useStyle(DrawingStyle.DROP_PREVIEW);
     93         if (be.isValid()) {
     94             // At least the first element has a bound. Draw rectangles
     95             // for all dropped elements with valid bounds, offset at
     96             // (0,0)
     97             for (IDragElement it : elements) {
     98                 Rect currBounds = it.getBounds();
     99                 if (currBounds.isValid()) {
    100                     int offsetX = b.x - currBounds.x;
    101                     int offsetY = b.y - currBounds.y;
    102                     drawElement(gc, it, offsetX, offsetY);
    103                 }
    104             }
    105         } else {
    106             // We don't have bounds for new elements. In this case
    107             // just draw insert lines indicating the top left corner where
    108             // the item will be placed
    109 
    110             // +1: Place lines fully within the view (the stroke width is 2) to
    111             // make
    112             // it even more visually obvious
    113             gc.drawLine(b.x + 1, b.y, b.x + 1, b.y + b.h);
    114             gc.drawLine(b.x, b.y + 1, b.x + b.w, b.y + 1);
    115         }
    116     }
    117 
    118     @Override
    119     public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
    120             @Nullable DropFeedback feedback, @NonNull Point p) {
    121         feedback.userData = p;
    122         feedback.requestPaint = true;
    123         return feedback;
    124     }
    125 
    126     @Override
    127     public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
    128             @Nullable DropFeedback feedback) {
    129         // ignore
    130     }
    131 
    132     @Override
    133     public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
    134             final @Nullable DropFeedback feedback, final @NonNull Point p) {
    135         Rect b = targetNode.getBounds();
    136         if (!b.isValid()) {
    137             return;
    138         }
    139 
    140         // Collect IDs from dropped elements and remap them to new IDs
    141         // if this is a copy or from a different canvas.
    142         final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
    143                 feedback.isCopy || !feedback.sameCanvas);
    144 
    145         targetNode.editXml("Add elements to FrameLayout", new INodeHandler() {
    146 
    147             @Override
    148             public void handle(@NonNull INode node) {
    149 
    150                 // Now write the new elements.
    151                 for (IDragElement element : elements) {
    152                     String fqcn = element.getFqcn();
    153 
    154                     INode newChild = targetNode.appendChild(fqcn);
    155 
    156                     // Copy all the attributes, modifying them as needed.
    157                     addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
    158 
    159                     addInnerElements(newChild, element, idMap);
    160                 }
    161             }
    162         });
    163     }
    164 
    165     @Override
    166     public void addLayoutActions(
    167             @NonNull List<RuleAction> actions,
    168             final @NonNull INode parentNode,
    169             final @NonNull List<? extends INode> children) {
    170         super.addLayoutActions(actions, parentNode, children);
    171         actions.add(RuleAction.createSeparator(25));
    172         actions.add(createMarginAction(parentNode, children));
    173         if (children != null && children.size() > 0) {
    174             actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
    175         }
    176     }
    177 
    178     @Override
    179     public void onChildInserted(@NonNull INode node, @NonNull INode parent,
    180             @NonNull InsertType insertType) {
    181         // Look at the fill preferences and fill embedded layouts etc
    182         String fqcn = node.getFqcn();
    183         IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
    184         if (metadata != null) {
    185             FillPreference fill = metadata.getFillPreference();
    186             String fillParent = getFillParentValueName();
    187             if (fill.fillHorizontally(true)) {
    188                 node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
    189             }
    190             if (fill.fillVertically(false)) {
    191                 node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
    192             }
    193         }
    194     }
    195 }
    196