• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17package com.android.adt.gscripts;
18
19/**
20 * An {@link IViewRule} for android.widget.LinearLayout and all its derived classes.
21 */
22public 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