Home | History | Annotate | Download | only in gscripts
      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.adt.gscripts;
     18 
     19 /**
     20  * An {@link IViewRule} for android.widget.LinearLayout and all its derived classes.
     21  */
     22 public class AndroidWidgetLinearLayoutRule extends BaseLayout {
     23 
     24     public static String ATTR_ORIENTATION = "orientation";
     25     public static String VALUE_VERTICAL = "vertical";
     26 
     27     // ==== Drag'n'drop support ====
     28 
     29     DropFeedback onDropEnter(INode targetNode, IDragElement[] elements) {
     30 
     31         if (elements.length == 0) {
     32             return null;
     33         }
     34 
     35         def bn = targetNode.getBounds();
     36         if (!bn.isValid()) {
     37             return;
     38         }
     39 
     40         boolean isVertical =
     41             targetNode.getStringAttr(ANDROID_URI, ATTR_ORIENTATION) == VALUE_VERTICAL;
     42 
     43         // Prepare a list of insertion points: X coords for horizontal, Y for vertical.
     44         // Each list is a tuple: 0=pixel coordinate, 1=index of children or -1 for "at end".
     45         def indexes = [ ];
     46 
     47         int last = isVertical ? bn.y : bn.x;
     48         int pos = 0;
     49         targetNode.getChildren().each {
     50             def bc = it.getBounds();
     51             if (bc.isValid()) {
     52                 // add an insertion point between the last point and the start of this child
     53                 int v = isVertical ? bc.y : bc.x;
     54                 v = (last + v) / 2;
     55                 indexes.add( [v, pos++] );
     56 
     57                 last = isVertical ? (bc.y + bc.h) : (bc.x + bc.w);
     58             }
     59         }
     60 
     61         int v = isVertical ? (bn.y + bn.h) : (bn.x + bn.w);
     62         v = (last + v) / 2;
     63         indexes.add( [v, -1] );
     64 
     65         return new DropFeedback(
     66           [ "isVertical": isVertical,   // boolean: True if vertical linear layout
     67             "indexes": indexes,         // list(tuple(0:int, 1:int)): insert points (pixels + index)
     68             "currX": null,              // int: Current marker X position
     69             "currY": null,              // int: Current marker Y position
     70             "insertPos": -1             // int: Current drop insert index (-1 for "at the end")
     71           ],
     72           {
     73             gc, node, feedback ->
     74             // Paint closure for the LinearLayout.
     75             // This is called by the canvas when a draw is needed.
     76 
     77             drawFeedback(gc, node, elements, feedback);
     78         });
     79     }
     80 
     81     void drawFeedback(IGraphics gc,
     82                       INode node,
     83                       IDragElement[] elements,
     84                       DropFeedback feedback) {
     85         Rect b = node.getBounds();
     86         if (!b.isValid()) {
     87             return;
     88         }
     89 
     90         // Highlight the receiver
     91         gc.setForeground(gc.registerColor(0x00FFFF00));
     92         gc.setLineStyle(IGraphics.LineStyle.LINE_SOLID);
     93         gc.setLineWidth(2);
     94         gc.drawRect(b);
     95 
     96         gc.setLineStyle(IGraphics.LineStyle.LINE_DOT);
     97         gc.setLineWidth(1);
     98 
     99         def indexes = feedback.userData.indexes;
    100         boolean isVertical = feedback.userData.isVertical;
    101 
    102         indexes.each {
    103             int i = it[0];
    104             if (isVertical) {
    105                 // draw horizontal lines
    106                 gc.drawLine(b.x, i, b.x + b.w, i);
    107             } else {
    108                 // draw vertical lines
    109                 gc.drawLine(i, b.y, i, b.y + b.h);
    110             }
    111         }
    112 
    113         def currX = feedback.userData.currX;
    114         def currY = feedback.userData.currY;
    115 
    116         if (currX != null && currY != null) {
    117             int x = currX;
    118             int y = currY;
    119 
    120             // Draw a mark at the drop point.
    121             gc.setLineStyle(IGraphics.LineStyle.LINE_SOLID);
    122             gc.setLineWidth(2);
    123 
    124             gc.drawLine(x - 10, y - 10, x + 10, y + 10);
    125             gc.drawLine(x + 10, y - 10, x - 10, y + 10);
    126             gc.drawOval(x - 10, y - 10, x + 10, y + 10);
    127 
    128             Rect be = elements[0].getBounds();
    129 
    130             if (be.isValid()) {
    131                 // At least the first element has a bound. Draw rectangles
    132                 // for all dropped elements with valid bounds, offset at
    133                 // the drop point.
    134 
    135                 int offsetX = x - be.x;
    136                 int offsetY = y - be.y;
    137 
    138                 // If there's a parent, keep the X/Y coordinate the same relative to the parent.
    139                 Rect pb = elements[0].getParentBounds();
    140                 if (pb.isValid()) {
    141                     if (isVertical) {
    142                         offsetX = b.x - pb.x;
    143                     } else {
    144                         offsetY = b.y - pb.y;
    145                     }
    146                 }
    147 
    148                 for (element in elements) {
    149                     drawElement(gc, element, offsetX, offsetY);
    150                 }
    151             }
    152         }
    153     }
    154 
    155     DropFeedback onDropMove(INode targetNode,
    156                             IDragElement[] elements,
    157                             DropFeedback feedback,
    158                             Point p) {
    159         def data = feedback.userData;
    160 
    161         Rect b = targetNode.getBounds();
    162         if (!b.isValid()) {
    163             return feedback;
    164         }
    165 
    166         boolean isVertical = data.isVertical;
    167 
    168         int bestDist = Integer.MAX_VALUE;
    169         int bestIndex = Integer.MIN_VALUE;
    170         int bestPos = null;
    171 
    172         for(index in data.indexes) {
    173             int i   = index[0];
    174             int pos = index[1];
    175             int dist = (isVertical ? p.y : p.x) - i;
    176             if (dist < 0) dist = - dist;
    177             if (dist < bestDist) {
    178                 bestDist = dist;
    179                 bestIndex = i;
    180                 bestPos = pos;
    181                 if (bestDist <= 0) break;
    182             }
    183         }
    184 
    185         if (bestIndex != Integer.MIN_VALUE) {
    186             def old_x = data.currX;
    187             def old_y = data.currY;
    188 
    189             if (isVertical) {
    190                 data.currX = b.x + b.w / 2;
    191                 data.currY = bestIndex;
    192             } else {
    193                 data.currX = bestIndex;
    194                 data.currY = b.y + b.h / 2;
    195             }
    196 
    197             data.insertPos = bestPos;
    198 
    199             feedback.requestPaint = (old_x != data.currX) || (old_y != data.currY);
    200         }
    201 
    202         return feedback;
    203     }
    204 
    205     void onDropLeave(INode targetNode, IDragElement[] elements, DropFeedback feedback) {
    206         // ignore
    207     }
    208 
    209     void onDropped(INode targetNode,
    210                    IDragElement[] elements,
    211                    DropFeedback feedback,
    212                    Point p) {
    213 
    214         int insertPos = feedback.userData.insertPos;
    215 
    216         // Collect IDs from dropped elements and remap them to new IDs
    217         // if this is a copy or from a different canvas.
    218         def idMap = getDropIdMap(targetNode, elements, feedback.isCopy || !feedback.sameCanvas);
    219 
    220         targetNode.editXml("Add elements to LinearLayout") {
    221 
    222             // Now write the new elements.
    223             for (element in elements) {
    224                 String fqcn = element.getFqcn();
    225                 Rect be = element.getBounds();
    226 
    227                 INode newChild = targetNode.insertChildAt(fqcn, insertPos);
    228 
    229                 // insertPos==-1 means to insert at the end. Otherwise
    230                 // increment the insertion position.
    231                 if (insertPos >= 0) {
    232                     insertPos++;
    233                 }
    234 
    235                 // Copy all the attributes, modifying them as needed.
    236                 def attrFilter = getLayoutAttrFilter();
    237                 addAttributes(newChild, element, idMap) {
    238                     uri, name, value ->
    239                     // TODO need a better way to exclude other layout attributes dynamically
    240                     if (uri == ANDROID_URI && name in attrFilter) {
    241                         return false; // don't set these attributes
    242                     } else {
    243                         return value;
    244                     }
    245                 };
    246 
    247                 addInnerElements(newChild, element, idMap);
    248             }
    249         }
    250 
    251 
    252     }
    253 }
    254