1 /* 2 * Copyright (C) 2009 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.api; 18 19 import com.android.annotations.NonNull; 20 import com.android.annotations.Nullable; 21 22 import java.util.List; 23 24 25 /** 26 * An {@link IViewRule} describes the rules that apply to a given Layout or View object 27 * in the Graphical Layout Editor. 28 * <p/> 29 * Rules are implemented by builtin layout helpers, or 3rd party layout rule implementations 30 * provided with or for a given 3rd party widget. 31 * <p/> 32 * A 3rd party layout rule should use the same fully qualified class name as the layout it 33 * represents, plus "Rule" as a suffix. For example, the layout rule for the 34 * LinearLayout class is LinearLayoutRule, in the same package. 35 * <p/> 36 * Rule instances are stateless. They are created once per View class to handle and are shared 37 * across platforms or editor instances. As such, rules methods should never cache editor-specific 38 * arguments that they might receive. 39 * <p/> 40 * <b>NOTE: This is not a public or final API; if you rely on this be prepared 41 * to adjust your code for the next tools release.</b> 42 * </p> 43 */ 44 public interface IViewRule { 45 46 /** 47 * This method is called by the rule engine when the script is first loaded. 48 * It gives the rule a chance to initialize itself. 49 * 50 * @param fqcn The fully qualified class name of the Layout or View that will be managed by 51 * this rule. This can be cached as it will never change for the lifetime of this rule 52 * instance. This may or may not match the script's filename as it may be the fqcn of a 53 * class derived from the one this rule can handle. 54 * @param engine The engine that is managing the rules. A rule can store a reference to 55 * the engine during initialization and then use it later to invoke some of the 56 * {@link IClientRulesEngine} methods for example to request user input. 57 * @return True if this rule can handle the given FQCN. False if the rule can't handle the 58 * given FQCN, in which case the rule engine will find another rule matching a parent class. 59 */ onInitialize(@onNull String fqcn, @NonNull IClientRulesEngine engine)60 boolean onInitialize(@NonNull String fqcn, @NonNull IClientRulesEngine engine); 61 62 /** 63 * This method is called by the rules engine just before the script is unloaded. 64 */ onDispose()65 void onDispose(); 66 67 /** 68 * Returns the class name to display when an element is selected in the layout editor. 69 * <p/> 70 * If null is returned, the layout editor will automatically shorten the class name using its 71 * own heuristic, which is to keep the first 2 package components and the class name. 72 * The class name is the <code>fqcn</code> argument that was given 73 * to {@link #onInitialize(String,IClientRulesEngine)}. 74 * 75 * @return Null for the default behavior or a shortened string. 76 */ 77 @Nullable getDisplayName()78 String getDisplayName(); 79 80 /** 81 * Invoked by the Rules Engine to produce a set of actions to customize 82 * the context menu displayed for this view. The result is not cached and the 83 * method is invoked every time the context menu is about to be shown. 84 * <p> 85 * The order of the menu items is determined by the sort priority set on 86 * the actions. 87 * <p/> 88 * Most rules should consider calling super.{@link #addContextMenuActions(List, INode)} 89 * as well. 90 * <p/> 91 * Menu actions are either toggles or fixed lists with one currently-selected 92 * item. It is expected that the rule will need to recreate the actions with 93 * different selections when a menu is going to shown, which is why the result 94 * is not cached. However rules are encouraged to cache some or all of the result 95 * to speed up following calls if it makes sense. 96 * 97 * @param actions a list of actions to add new context menu actions into. The order 98 * of the actions in this list is not important; it will be sorted by 99 * {@link RuleAction#getSortPriority()} later. 100 * @param node the node to add actions for. 101 */ addContextMenuActions(@onNull List<RuleAction> actions, @NonNull INode node)102 void addContextMenuActions(@NonNull List<RuleAction> actions, @NonNull INode node); 103 104 /** 105 * Returns the id of the default action to invoke for this view, typically when the 106 * user presses F2. The id should correspond to the {@link RuleAction#getId()} returned 107 * by one of the actions added by {@link #addContextMenuActions(List, INode)}. 108 * 109 * @param node the primary selected node 110 * @return the id of default action, or null if none is default 111 */ 112 @Nullable getDefaultActionId(@onNull INode node)113 String getDefaultActionId(@NonNull INode node); 114 115 /** 116 * Invoked by the Rules Engine to ask the parent layout for the set of layout actions 117 * to display in the layout bar. The layout rule should add these into the provided 118 * list. The order the items are added in does not matter; the 119 * {@link RuleAction#getSortPriority()} values will be used to sort the actions prior 120 * to display, which makes it easier for parent rules and deriving rules to interleave 121 * their respective actions. 122 * 123 * @param actions the list of actions to add newly registered actions into 124 * @param parentNode the parent of the selection, or the selection itself if the root 125 * @param targets the targeted/selected nodes, if any 126 */ addLayoutActions( @onNull List<RuleAction> actions, @NonNull INode parentNode, @NonNull List<? extends INode> targets)127 void addLayoutActions( 128 @NonNull List<RuleAction> actions, 129 @NonNull INode parentNode, 130 @NonNull List<? extends INode> targets); 131 132 // ==== Selection ==== 133 134 /** 135 * Returns a list of strings that will be displayed when a single child is being 136 * selected in a layout corresponding to this rule. This gives the container a chance 137 * to describe the child's layout attributes or other relevant information. 138 * <p/> 139 * Note that this is called only for single selections. 140 * <p/> 141 * 142 * @param parentNode The parent of the node selected. Never null. 143 * @param childNode The child node that was selected. Never null. 144 * @return a list of strings to be displayed, or null or empty to display nothing 145 */ 146 @Nullable getSelectionHint(@onNull INode parentNode, @NonNull INode childNode)147 List<String> getSelectionHint(@NonNull INode parentNode, @NonNull INode childNode); 148 149 /** 150 * Paints any layout-specific selection feedback for the given parent layout. 151 * 152 * @param graphics the graphics context to paint into 153 * @param parentNode the parent layout node 154 * @param childNodes the child nodes selected in the parent layout 155 * @param view An instance of the view to be painted (may be null) 156 */ paintSelectionFeedback( @onNull IGraphics graphics, @NonNull INode parentNode, @NonNull List<? extends INode> childNodes, @Nullable Object view)157 void paintSelectionFeedback( 158 @NonNull IGraphics graphics, 159 @NonNull INode parentNode, 160 @NonNull List<? extends INode> childNodes, 161 @Nullable Object view); 162 163 // ==== Drag'n'drop support ==== 164 165 /** 166 * Called when the d'n'd starts dragging over the target node. If 167 * interested, returns a DropFeedback passed to onDrop/Move/Leave/Paint. If 168 * not interested in drop, return null. Followed by a paint. 169 * 170 * @param targetNode the {@link INode} for the target layout receiving a 171 * drop event 172 * @param targetView the corresponding View object for the target layout, or 173 * null if not known 174 * @param elements an array of {@link IDragElement} element descriptors for 175 * the dragged views. When there are more than one element, the 176 * first element will always be the "primary" element (e.g. the 177 * one that the mouse is actively dragging.) 178 * @return a {@link DropFeedback} object with drop state (which will be 179 * supplied to a follow-up {@link #onDropMove} call), or null if the 180 * drop should be ignored 181 */ 182 @Nullable onDropEnter(@onNull INode targetNode, @Nullable Object targetView, @Nullable IDragElement[] elements)183 DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView, 184 @Nullable IDragElement[] elements); 185 186 /** 187 * Called after onDropEnter. Returns a DropFeedback passed to 188 * onDrop/Move/Leave/Paint (typically same as input one). Returning null 189 * will invalidate the drop workflow. 190 * 191 * @param targetNode the {@link INode} for the target layout receiving a 192 * drop event 193 * @param elements an array of {@link IDragElement} element descriptors for 194 * the dragged views. When there are more than one element, the 195 * first element will always be the "primary" element (e.g. the 196 * one that the mouse is actively dragging.) 197 * @param feedback the {@link DropFeedback} object created by 198 * {@link #onDropEnter(INode, Object, IDragElement[])} 199 * @param where the current mouse drag position 200 * @return a {@link DropFeedback} (which is usually just the same one passed 201 * into this method) 202 */ 203 @Nullable onDropMove( @onNull INode targetNode, @NonNull IDragElement[] elements, @Nullable DropFeedback feedback, @NonNull Point where)204 DropFeedback onDropMove( 205 @NonNull INode targetNode, 206 @NonNull IDragElement[] elements, 207 @Nullable DropFeedback feedback, 208 @NonNull Point where); 209 210 /** 211 * Called when drop leaves the target without actually dropping. 212 * <p/> 213 * When switching between views, onDropLeave is called on the old node *after* onDropEnter 214 * is called after a new node that returned a non-null feedback. The feedback received here 215 * is the one given by the previous onDropEnter on the same target. 216 * <p/> 217 * E.g. call order is: 218 * <pre> 219 * - onDropEnter(node1) => feedback1 220 * <i>...user moves to new view...</i> 221 * - onDropEnter(node2) => feedback2 222 * - onDropLeave(node1, feedback1) 223 * <i>...user leaves canvas...</i> 224 * - onDropLeave(node2, feedback2) 225 * </pre> 226 * @param targetNode the {@link INode} for the target layout receiving a 227 * drop event 228 * @param elements an array of {@link IDragElement} element descriptors for 229 * the dragged views. When there are more than one element, the 230 * first element will always be the "primary" element (e.g. the 231 * one that the mouse is actively dragging.) 232 * @param feedback the {@link DropFeedback} object created by 233 * {@link #onDropEnter(INode, Object, IDragElement[])} 234 */ onDropLeave( @onNull INode targetNode, @NonNull IDragElement[] elements, @Nullable DropFeedback feedback)235 void onDropLeave( 236 @NonNull INode targetNode, 237 @NonNull IDragElement[] elements, 238 @Nullable DropFeedback feedback); 239 240 /** 241 * Called when drop is released over the target to perform the actual drop. 242 * <p> 243 * TODO: Document that this method will be called under an edit lock so you can 244 * directly manipulate the nodes without wrapping it in an 245 * {@link INode#editXml(String, INodeHandler)} call. 246 * 247 * @param targetNode the {@link INode} for the target layout receiving a 248 * drop event 249 * @param elements an array of {@link IDragElement} element descriptors for 250 * the dragged views. When there are more than one element, the 251 * first element will always be the "primary" element (e.g. the 252 * one that the mouse is actively dragging.) 253 * @param feedback the {@link DropFeedback} object created by 254 * {@link #onDropEnter(INode, Object, IDragElement[])} 255 * @param where the mouse drop position 256 */ onDropped( @onNull INode targetNode, @NonNull IDragElement[] elements, @Nullable DropFeedback feedback, @NonNull Point where)257 void onDropped( 258 @NonNull INode targetNode, 259 @NonNull IDragElement[] elements, 260 @Nullable DropFeedback feedback, 261 @NonNull Point where); 262 263 /** 264 * Called when pasting elements in an existing document on the selected target. 265 * 266 * @param targetNode The first node selected. 267 * @param targetView the corresponding View object for the target layout, or 268 * null if not known 269 * @param pastedElements The elements being pasted. 270 */ onPaste(@onNull INode targetNode, @Nullable Object targetView, @NonNull IDragElement[] pastedElements)271 void onPaste(@NonNull INode targetNode, @Nullable Object targetView, 272 @NonNull IDragElement[] pastedElements); 273 274 // ==== XML Creation ==== 275 276 /** 277 * Called when a view for this rule is being created. This allows for the rule to 278 * customize the newly created object. Note that this method is called not just when a 279 * view is created from a palette drag, but when views are constructed via a drag-move 280 * (where views are created in the destination and then deleted from the source), and 281 * even when views are constructed programmatically from other view rules. The 282 * {@link InsertType} parameter can be used to distinguish the context for the 283 * insertion. For example, the <code>DialerFilterRule</code> will insert EditText children 284 * when a DialerFilter is first created, but not during a copy/paste or a move. 285 * 286 * @param node the newly created node (which will always be a View that applies to 287 * this {@link IViewRule}) 288 * @param parent the parent of the node (which may not yet contain the newly created 289 * node in its child list) 290 * @param insertType whether this node was created as part of a newly created view, or 291 * as a copy, or as a move, etc. 292 */ onCreate(@onNull INode node, @NonNull INode parent, @NonNull InsertType insertType)293 void onCreate(@NonNull INode node, @NonNull INode parent, @NonNull InsertType insertType); 294 295 /** 296 * Called when a child for this view has been created and is being inserted into the 297 * view parent for which this {@link IViewRule} applies. Allows the parent to perform 298 * customizations of the object. As with {@link #onCreate}, the {@link InsertType} 299 * parameter can be used to handle new creation versus moves versus copy/paste 300 * operations differently. 301 * 302 * @param child the newly created node 303 * @param parent the parent of the newly created node (which may not yet contain the 304 * newly created node in its child list) 305 * @param insertType whether this node was created as part of a newly created view, or 306 * as a copy, or as a move, etc. 307 */ onChildInserted(@onNull INode child, @NonNull INode parent, @NonNull InsertType insertType)308 void onChildInserted(@NonNull INode child, @NonNull INode parent, 309 @NonNull InsertType insertType); 310 311 /** 312 * Called when one or more children are about to be deleted by the user. 313 * Note that children deleted programmatically from view rules (via 314 * {@link INode#removeChild(INode)}) will not notify about deletion. 315 * <p> 316 * Note that this method will be called under an edit lock, so rules can 317 * directly add/remove nodes and attributes as part of the deletion handling 318 * (and their actions will be part of the same undo-unit.) 319 * <p> 320 * Note that when children are moved (such as when you drag a child within a 321 * LinearLayout to move it from one position among the children to another), 322 * that will also result in a 323 * {@link #onChildInserted(INode, INode, InsertType)} (with the 324 * {@code InsertType} set to {@link InsertType#MOVE_WITHIN}) and a remove 325 * via this {@link #onRemovingChildren(List, INode, boolean)} method. When 326 * the deletion is occurring as part of a local move (insert + delete), the 327 * {@code moved} parameter to this method is set to true. 328 * 329 * @param deleted a nonempty list of children about to be deleted 330 * @param parent the parent of the deleted children (which still contains 331 * the children since this method is called before the deletion 332 * is performed) 333 * @param moved when true, the nodes are being deleted as part of a local 334 * move (where copies are inserted elsewhere) 335 */ onRemovingChildren(@onNull List<INode> deleted, @NonNull INode parent, boolean moved)336 void onRemovingChildren(@NonNull List<INode> deleted, @NonNull INode parent, 337 boolean moved); 338 339 /** 340 * Called by the IDE on the parent layout when a child widget is being resized. This 341 * is called once at the beginning of the resizing operation. A horizontal edge, 342 * or a vertical edge, or both, can be resized simultaneously. 343 * 344 * @param child the widget being resized 345 * @param parent the layout containing the child 346 * @param horizEdge The horizontal edge being resized, or null 347 * @param verticalEdge the vertical edge being resized, or null 348 * @param childView an instance of the resized node view, or null if not known 349 * @param parentView an instance of the parent layout view object, or null if not known 350 * @return a {@link DropFeedback} object which performs an update painter callback 351 * etc. 352 */ 353 @Nullable onResizeBegin( @onNull INode child, @NonNull INode parent, @Nullable SegmentType horizEdge, @Nullable SegmentType verticalEdge, @Nullable Object childView, @Nullable Object parentView)354 DropFeedback onResizeBegin( 355 @NonNull INode child, 356 @NonNull INode parent, 357 @Nullable SegmentType horizEdge, 358 @Nullable SegmentType verticalEdge, 359 @Nullable Object childView, 360 @Nullable Object parentView); 361 362 /** 363 * Called by the IDE on the parent layout when a child widget is being resized. This 364 * is called repeatedly during the resize as the mouse is dragged to update the drag 365 * bounds, recompute guidelines, etc. The resize has not yet been "committed" so the 366 * XML should not be edited yet. 367 * 368 * @param feedback the {@link DropFeedback} object created in {@link #onResizeBegin} 369 * @param child the widget being resized 370 * @param parent the layout containing the child 371 * @param newBounds the new bounds the user has chosen to resize the widget to, 372 * in absolute coordinates 373 * @param modifierMask The modifier keys currently pressed by the user, as a bitmask 374 * of the constants {@link DropFeedback#MODIFIER1}, {@link DropFeedback#MODIFIER2} 375 * and {@link DropFeedback#MODIFIER3}. 376 */ onResizeUpdate( @ullable DropFeedback feedback, @NonNull INode child, @NonNull INode parent, @NonNull Rect newBounds, int modifierMask)377 void onResizeUpdate( 378 @Nullable DropFeedback feedback, 379 @NonNull INode child, 380 @NonNull INode parent, 381 @NonNull Rect newBounds, 382 int modifierMask); 383 384 /** 385 * Called by the IDE on the parent layout when a child widget is being resized. This 386 * is called once at the end of the resize operation, if it was not canceled. 387 * This method can call {@link INode#editXml} to update the node to reflect the 388 * new bounds. 389 * 390 * @param feedback the {@link DropFeedback} object created in {@link #onResizeBegin} 391 * @param child the widget being resized 392 * @param parent the layout containing the child 393 * @param newBounds the new bounds the user has chosen to resize the widget to, 394 * in absolute coordinates 395 */ onResizeEnd( @ullable DropFeedback feedback, @NonNull INode child, @NonNull INode parent, @NonNull Rect newBounds)396 void onResizeEnd( 397 @Nullable DropFeedback feedback, 398 @NonNull INode child, 399 @NonNull INode parent, 400 @NonNull Rect newBounds); 401 } 402