aboutsummaryrefslogtreecommitdiffstats
path: root/rule_api/src/com/android/ide/common/api/IViewRule.java
blob: bcf4e89cc89c53d62b86d5f3a5c73ead3f906170 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.common.api;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;

import java.util.List;


/**
 * An {@link IViewRule} describes the rules that apply to a given Layout or View object
 * in the Graphical Layout Editor.
 * <p/>
 * Rules are implemented by builtin layout helpers, or 3rd party layout rule implementations
 * provided with or for a given 3rd party widget.
 * <p/>
 * A 3rd party layout rule should use the same fully qualified class name as the layout it
 * represents, plus "Rule" as a suffix. For example, the layout rule for the
 * LinearLayout class is LinearLayoutRule, in the same package.
 * <p/>
 * Rule instances are stateless. They are created once per View class to handle and are shared
 * across platforms or editor instances. As such, rules methods should never cache editor-specific
 * arguments that they might receive.
 * <p/>
 * <b>NOTE: This is not a public or final API; if you rely on this be prepared
 * to adjust your code for the next tools release.</b>
 * </p>
 */
public interface IViewRule {

    /**
     * This method is called by the rule engine when the script is first loaded.
     * It gives the rule a chance to initialize itself.
     *
     * @param fqcn The fully qualified class name of the Layout or View that will be managed by
     *   this rule. This can be cached as it will never change for the lifetime of this rule
     *   instance. This may or may not match the script's filename as it may be the fqcn of a
     *   class derived from the one this rule can handle.
     * @param engine The engine that is managing the rules. A rule can store a reference to
     *   the engine during initialization and then use it later to invoke some of the
     *   {@link IClientRulesEngine} methods for example to request user input.
     * @return True if this rule can handle the given FQCN. False if the rule can't handle the
     *   given FQCN, in which case the rule engine will find another rule matching a parent class.
     */
    boolean onInitialize(@NonNull String fqcn, @NonNull IClientRulesEngine engine);

    /**
     * This method is called by the rules engine just before the script is unloaded.
     */
    void onDispose();

    /**
     * Returns the class name to display when an element is selected in the layout editor.
     * <p/>
     * If null is returned, the layout editor will automatically shorten the class name using its
     * own heuristic, which is to keep the first 2 package components and the class name.
     * The class name is the <code>fqcn</code> argument that was given
     * to {@link #onInitialize(String,IClientRulesEngine)}.
     *
     * @return Null for the default behavior or a shortened string.
     */
    @Nullable
    String getDisplayName();

    /**
     * Invoked by the Rules Engine to produce a set of actions to customize
     * the context menu displayed for this view. The result is not cached and the
     * method is invoked every time the context menu is about to be shown.
     * <p>
     * The order of the menu items is determined by the sort priority set on
     * the actions.
     * <p/>
     * Most rules should consider calling super.{@link #addContextMenuActions(List, INode)}
     * as well.
     * <p/>
     * Menu actions are either toggles or fixed lists with one currently-selected
     * item. It is expected that the rule will need to recreate the actions with
     * different selections when a menu is going to shown, which is why the result
     * is not cached. However rules are encouraged to cache some or all of the result
     * to speed up following calls if it makes sense.
     *
     * @param actions a list of actions to add new context menu actions into. The order
     *    of the actions in this list is not important; it will be sorted by
     *    {@link RuleAction#getSortPriority()} later.
     * @param node the node to add actions for.
     */
    void addContextMenuActions(@NonNull List<RuleAction> actions, @NonNull INode node);

    /**
     * Invoked by the Rules Engine to ask the parent layout for the set of layout actions
     * to display in the layout bar. The layout rule should add these into the provided
     * list. The order the items are added in does not matter; the
     * {@link RuleAction#getSortPriority()} values will be used to sort the actions prior
     * to display, which makes it easier for parent rules and deriving rules to interleave
     * their respective actions.
     *
     * @param actions the list of actions to add newly registered actions into
     * @param parentNode the parent of the selection, or the selection itself if the root
     * @param targets the targeted/selected nodes, if any
     */
    void addLayoutActions(
            @NonNull List<RuleAction> actions,
            @NonNull INode parentNode,
            @NonNull List<? extends INode> targets);

    // ==== Selection ====

    /**
     * Returns a list of strings that will be displayed when a single child is being
     * selected in a layout corresponding to this rule. This gives the container a chance
     * to describe the child's layout attributes or other relevant information.
     * <p/>
     * Note that this is called only for single selections.
     * <p/>
     *
     * @param parentNode The parent of the node selected. Never null.
     * @param childNode The child node that was selected. Never null.
     * @return a list of strings to be displayed, or null or empty to display nothing
     */
    @Nullable
    List<String> getSelectionHint(@NonNull INode parentNode, @NonNull INode childNode);

    /**
     * Paints any layout-specific selection feedback for the given parent layout.
     *
     * @param graphics the graphics context to paint into
     * @param parentNode the parent layout node
     * @param childNodes the child nodes selected in the parent layout
     * @param view An instance of the view to be painted (may be null)
     */
    void paintSelectionFeedback(
            @NonNull IGraphics graphics,
            @NonNull INode parentNode,
            @NonNull List<? extends INode> childNodes,
            @Nullable Object view);

    // ==== Drag'n'drop support ====

    /**
     * Called when the d'n'd starts dragging over the target node. If
     * interested, returns a DropFeedback passed to onDrop/Move/Leave/Paint. If
     * not interested in drop, return null. Followed by a paint.
     *
     * @param targetNode the {@link INode} for the target layout receiving a
     *            drop event
     * @param targetView the corresponding View object for the target layout, or
     *            null if not known
     * @param elements an array of {@link IDragElement} element descriptors for
     *            the dragged views
     * @return a {@link DropFeedback} object with drop state (which will be
     *         supplied to a follow-up {@link #onDropMove} call), or null if the
     *         drop should be ignored
     */
    @Nullable
    DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
            @Nullable IDragElement[] elements);

    /**
     * Called after onDropEnter. Returns a DropFeedback passed to
     * onDrop/Move/Leave/Paint (typically same as input one). Returning null
     * will invalidate the drop workflow.
     *
     * @param targetNode the {@link INode} for the target layout receiving a
     *            drop event
     * @param elements an array of {@link IDragElement} element descriptors for
     *            the dragged views
     * @param feedback the {@link DropFeedback} object created by
     *            {@link #onDropEnter(INode, Object, IDragElement[])}
     * @param where the current mouse drag position
     * @return a {@link DropFeedback} (which is usually just the same one passed
     *         into this method)
     */
    @Nullable
    DropFeedback onDropMove(
            @NonNull INode targetNode,
            @NonNull IDragElement[] elements,
            @Nullable DropFeedback feedback,
            @NonNull Point where);

    /**
     * Called when drop leaves the target without actually dropping.
     * <p/>
     * When switching between views, onDropLeave is called on the old node *after* onDropEnter
     * is called after a new node that returned a non-null feedback. The feedback received here
     * is the one given by the previous onDropEnter on the same target.
     * <p/>
     * E.g. call order is:
     * <pre>
     * - onDropEnter(node1) => feedback1
     * <i>...user moves to new view...</i>
     * - onDropEnter(node2) => feedback2
     * - onDropLeave(node1, feedback1)
     * <i>...user leaves canvas...</i>
     * - onDropLeave(node2, feedback2)
     * </pre>
     * @param targetNode the {@link INode} for the target layout receiving a
     *            drop event
     * @param elements an array of {@link IDragElement} element descriptors for
     *            the dragged views
     * @param feedback the {@link DropFeedback} object created by
     *            {@link #onDropEnter(INode, Object, IDragElement[])}
     */
    void onDropLeave(
            @NonNull INode targetNode,
            @NonNull IDragElement[] elements,
            @Nullable DropFeedback feedback);

    /**
     * Called when drop is released over the target to perform the actual drop.
     * <p>
     * TODO: Document that this method will be called under an edit lock so you can
     * directly manipulate the nodes without wrapping it in an
     * {@link INode#editXml(String, INodeHandler)} call.
     *
     * @param targetNode the {@link INode} for the target layout receiving a
     *            drop event
     * @param elements an array of {@link IDragElement} element descriptors for
     *            the dragged views
     * @param feedback the {@link DropFeedback} object created by
     *            {@link #onDropEnter(INode, Object, IDragElement[])}
     * @param where the mouse drop position
     */
    void onDropped(
            @NonNull INode targetNode,
            @NonNull IDragElement[] elements,
            @Nullable DropFeedback feedback,
            @NonNull Point where);

    /**
     * Called when pasting elements in an existing document on the selected target.
     *
     * @param targetNode The first node selected.
     * @param targetView the corresponding View object for the target layout, or
     *            null if not known
     * @param pastedElements The elements being pasted.
     */
    void onPaste(@NonNull INode targetNode, @Nullable Object targetView,
            @NonNull IDragElement[] pastedElements);

    // ==== XML Creation ====

    /**
     * Called when a view for this rule is being created. This allows for the rule to
     * customize the newly created object. Note that this method is called not just when a
     * view is created from a palette drag, but when views are constructed via a drag-move
     * (where views are created in the destination and then deleted from the source), and
     * even when views are constructed programmatically from other view rules. The
     * {@link InsertType} parameter can be used to distinguish the context for the
     * insertion. For example, the <code>DialerFilterRule</code> will insert EditText children
     * when a DialerFilter is first created, but not during a copy/paste or a move.
     *
     * @param node the newly created node (which will always be a View that applies to
     *            this {@link IViewRule})
     * @param parent the parent of the node (which may not yet contain the newly created
     *            node in its child list)
     * @param insertType whether this node was created as part of a newly created view, or
     *            as a copy, or as a move, etc.
     */
    void onCreate(@NonNull INode node, @NonNull INode parent, @NonNull InsertType insertType);

    /**
     * Called when a child for this view has been created and is being inserted into the
     * view parent for which this {@link IViewRule} applies. Allows the parent to perform
     * customizations of the object. As with {@link #onCreate}, the {@link InsertType}
     * parameter can be used to handle new creation versus moves versus copy/paste
     * operations differently.
     *
     * @param child the newly created node
     * @param parent the parent of the newly created node (which may not yet contain the
     *            newly created node in its child list)
     * @param insertType whether this node was created as part of a newly created view, or
     *            as a copy, or as a move, etc.
     */
    void onChildInserted(@NonNull INode child, @NonNull INode parent,
            @NonNull InsertType insertType);

    /**
     * Called when one or more children are about to be deleted by the user. Note that
     * children deleted programmatically from view rules (via
     * {@link INode#removeChild(INode)}) will not notify about deletion.
     * <p>
     * Note that this method will be called under an edit lock, so rules can directly
     * add/remove nodes and attributes as part of the deletion handling (and their
     * actions will be part of the same undo-unit.)
     *
     * @param deleted a nonempty list of children about to be deleted
     * @param parent the parent of the deleted children (which still contains the children
     *            since this method is called before the deletion is performed)
     */
    void onRemovingChildren(@NonNull List<INode> deleted, @NonNull INode parent);

    /**
     * Called by the IDE on the parent layout when a child widget is being resized. This
     * is called once at the beginning of the resizing operation. A horizontal edge,
     * or a vertical edge, or both, can be resized simultaneously.
     *
     * @param child the widget being resized
     * @param parent the layout containing the child
     * @param horizEdge The horizontal edge being resized, or null
     * @param verticalEdge the vertical edge being resized, or null
     * @param childView an instance of the resized node view, or null if not known
     * @param parentView an instance of the parent layout view object, or null if not known
     * @return a {@link DropFeedback} object which performs an update painter callback
     *         etc.
     */
    @Nullable
    DropFeedback onResizeBegin(
            @NonNull INode child,
            @NonNull INode parent,
            @Nullable SegmentType horizEdge,
            @Nullable SegmentType verticalEdge,
            @Nullable Object childView,
            @Nullable Object parentView);

    /**
     * Called by the IDE on the parent layout when a child widget is being resized. This
     * is called repeatedly during the resize as the mouse is dragged to update the drag
     * bounds, recompute guidelines, etc. The resize has not yet been "committed" so the
     * XML should not be edited yet.
     *
     * @param feedback the {@link DropFeedback} object created in {@link #onResizeBegin}
     * @param child the widget being resized
     * @param parent the layout containing the child
     * @param newBounds the new bounds the user has chosen to resize the widget to,
     *    in absolute coordinates
     * @param modifierMask The modifier keys currently pressed by the user, as a bitmask
     *    of the constants {@link DropFeedback#MODIFIER1}, {@link DropFeedback#MODIFIER2}
     *    and {@link DropFeedback#MODIFIER3}.
     */
    void onResizeUpdate(
            @Nullable DropFeedback feedback,
            @NonNull INode child,
            @NonNull INode parent,
            @NonNull Rect newBounds,
            int modifierMask);

    /**
     * Called by the IDE on the parent layout when a child widget is being resized. This
     * is called once at the end of the resize operation, if it was not canceled.
     * This method can call {@link INode#editXml} to update the node to reflect the
     * new bounds.
     *
     * @param feedback the {@link DropFeedback} object created in {@link #onResizeBegin}
     * @param child the widget being resized
     * @param parent the layout containing the child
     * @param newBounds the new bounds the user has chosen to resize the widget to,
     *    in absolute coordinates
     */
    void onResizeEnd(
            @Nullable DropFeedback feedback,
            @NonNull INode child,
            @NonNull INode parent,
            @NonNull Rect newBounds);
}