• 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 
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
onDropEnter(INode targetNode, Object targetView, final IDragElement[] elements)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