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