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