Home | History | Annotate | Download | only in api
      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      */
     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      */
     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
     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      */
    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
    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      */
    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
    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      */
    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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    396     void onResizeEnd(
    397             @Nullable DropFeedback feedback,
    398             @NonNull INode child,
    399             @NonNull INode parent,
    400             @NonNull Rect newBounds);
    401 }
    402