// The AbsoluteLayout accepts any drag'n'drop anywhere on its surface.
@Override
- public DropFeedback onDropEnter(final INode targetNode, final IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView,
+ final IDragElement[] elements) {
if (elements.length == 0) {
return null;
/** Rule for AdapterView subclasses that don't have more specific rules */
public class AdapterViewRule extends BaseLayoutRule {
@Override
- public DropFeedback onDropEnter(INode targetNode, IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView, IDragElement[] elements) {
// You are not allowed to insert children into AdapterViews; you must
// use the dedicated addView methods etc dynamically
DropFeedback dropFeedback = new DropFeedback(null, new IFeedbackPainter() {
* The default behavior for pasting in a layout is to simulate a drop in the
* top-left corner of the view.
* <p/>
- * Note that we explicitly do not call super() here -- the BasView.onPaste
+ * Note that we explicitly do not call super() here -- the BaseViewRule.onPaste handler
* will call onPasteBeforeChild() instead.
* <p/>
* Derived layouts should override this behavior if not appropriate.
*/
@Override
- public void onPaste(INode targetNode, IDragElement[] elements) {
-
- DropFeedback feedback = onDropEnter(targetNode, elements);
+ public void onPaste(INode targetNode, Object targetView, IDragElement[] elements) {
+ DropFeedback feedback = onDropEnter(targetNode, targetView, elements);
if (feedback != null) {
Point p = targetNode.getBounds().getTopLeft();
feedback = onDropMove(targetNode, elements, feedback, p);
* a hint to paste "before" it.
*
* @param parentNode the parent node we're pasting into
+ * @param parentView the view object for the parent layout, or null
* @param targetNode the first selected node
* @param elements the elements being pasted
*/
- public void onPasteBeforeChild(INode parentNode, INode targetNode, IDragElement[] elements) {
-
- DropFeedback feedback = onDropEnter(parentNode, elements);
+ public void onPasteBeforeChild(INode parentNode, Object parentView, INode targetNode,
+ IDragElement[] elements) {
+ DropFeedback feedback = onDropEnter(parentNode, parentView, elements);
if (feedback != null) {
Point parentP = parentNode.getBounds().getTopLeft();
Point targetP = targetNode.getBounds().getTopLeft();
// ---- Resizing ----
/** Creates a new {@link ResizeState} object to track resize state */
- protected ResizeState createResizeState(INode layout, INode node) {
- return new ResizeState(this, layout, node);
+ protected ResizeState createResizeState(INode layout, Object layoutView, INode node) {
+ return new ResizeState(this, layout, layoutView, node);
}
@Override
public DropFeedback onResizeBegin(INode child, INode parent,
- SegmentType horizontalEdge, SegmentType verticalEdge) {
- ResizeState state = createResizeState(parent, child);
+ SegmentType horizontalEdge, SegmentType verticalEdge,
+ Object childView, Object parentView) {
+ ResizeState state = createResizeState(parent, parentView, child);
state.horizontalEdgeType = horizontalEdge;
state.verticalEdgeType = verticalEdge;
// ==== Drag'n'drop support ====
// By default Views do not accept drag'n'drop.
- public DropFeedback onDropEnter(INode targetNode, IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView, IDragElement[] elements) {
return null;
}
* this case, defer the call to the parent layout and use the target node as
* an indication of where to paste.
*/
- public void onPaste(INode targetNode, IDragElement[] elements) {
+ public void onPaste(INode targetNode, Object targetView, IDragElement[] elements) {
//
INode parent = targetNode.getParent();
if (parent != null) {
IViewRule parentRule = mRulesEngine.loadRule(parentFqcn);
if (parentRule instanceof BaseLayoutRule) {
- ((BaseLayoutRule) parentRule).onPasteBeforeChild(parent, targetNode, elements);
+ ((BaseLayoutRule) parentRule).onPasteBeforeChild(parent, targetView, targetNode,
+ elements);
}
}
}
}
public void paintSelectionFeedback(IGraphics graphics, INode parentNode,
- List<? extends INode> childNodes) {
+ List<? extends INode> childNodes, Object view) {
}
// ---- Resizing ----
public DropFeedback onResizeBegin(INode child, INode parent, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
+ SegmentType verticalEdge, Object childView, Object parentView) {
return null;
}
import com.android.ide.common.api.INode;
import com.android.ide.common.api.INodeHandler;
import com.android.ide.common.api.IViewMetadata;
+import com.android.ide.common.api.IViewMetadata.FillPreference;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.IViewMetadata.FillPreference;
+import com.android.ide.common.api.RuleAction;
import com.android.util.Pair;
import java.util.List;
// The FrameLayout accepts any drag'n'drop anywhere on its surface.
@Override
- public DropFeedback onDropEnter(INode targetNode, final IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView,
+ final IDragElement[] elements) {
if (elements.length == 0) {
return null;
}
import com.android.ide.common.api.INode;
import com.android.ide.common.api.INodeHandler;
import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.RuleAction;
+import com.android.ide.common.api.RuleAction.Choices;
import com.android.ide.common.api.SegmentType;
import com.android.ide.common.layout.grid.GridDropHandler;
import com.android.ide.common.layout.grid.GridLayoutPainter;
return;
}
- GridModel grid = new GridModel(mRulesEngine, parentNode);
+ GridModel grid = new GridModel(mRulesEngine, parentNode, null);
if (id.equals(ACTION_ADD_ROW)) {
grid.addRow(children);
} else if (id.equals(ACTION_REMOVE_ROW)) {
}
@Override
- public DropFeedback onDropEnter(INode targetNode, final IDragElement[] elements) {
- GridDropHandler userData = new GridDropHandler(this, targetNode);
+ public DropFeedback onDropEnter(INode targetNode, Object targetView, IDragElement[] elements) {
+ GridDropHandler userData = new GridDropHandler(this, targetNode, targetView);
IFeedbackPainter painter = GridLayoutPainter.createDropFeedbackPainter(this, elements);
return new DropFeedback(userData, painter);
}
// Attempt to clean up spacer objects for any newly-empty rows or columns
// as the result of this deletion
- GridModel grid = new GridModel(mRulesEngine, parent);
+ GridModel grid = new GridModel(mRulesEngine, parent, null);
for (INode child : deleted) {
// We don't care about deletion of spacers
if (child.getFqcn().equals(FQCN_SPACE)) {
private GridModel getGrid(ResizeState resizeState) {
GridModel grid = (GridModel) resizeState.clientData;
if (grid == null) {
- grid = new GridModel(mRulesEngine, resizeState.layout);
+ grid = new GridModel(mRulesEngine, resizeState.layout, resizeState.layoutView);
resizeState.clientData = grid;
}
@Override
public void paintSelectionFeedback(IGraphics graphics, INode parentNode,
- List<? extends INode> childNodes) {
- super.paintSelectionFeedback(graphics, parentNode, childNodes);
+ List<? extends INode> childNodes, Object view) {
+ super.paintSelectionFeedback(graphics, parentNode, childNodes, view);
if (sShowStructure) {
// TODO: Cache the grid
- GridLayoutPainter.paintStructure(DrawingStyle.GUIDELINE_DASHED,
- parentNode, graphics, new GridModel(mRulesEngine, parentNode));
+ if (view != null) {
+ GridLayoutPainter.paintStructure(view, DrawingStyle.GUIDELINE_DASHED,
+ parentNode, graphics);
+ } else {
+ GridLayoutPainter.paintStructure(DrawingStyle.GUIDELINE_DASHED,
+ parentNode, graphics, new GridModel(mRulesEngine, parentNode, view));
+ }
} else if (sDebugGridLayout) {
GridLayoutPainter.paintStructure(DrawingStyle.GRID,
- parentNode, graphics, new GridModel(mRulesEngine, parentNode));
+ parentNode, graphics, new GridModel(mRulesEngine, parentNode, view));
}
// TBD: Highlight the cells around the selection, and display easy controls
*/
public abstract class IgnoredLayoutRule extends BaseLayoutRule {
@Override
- public DropFeedback onDropEnter(INode targetNode, IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView, IDragElement[] elements) {
// Do nothing; this layout rule corresponds to a layout that
// should not be handled as a layout by the visual editor - usually
// because some widget is extending a layout for implementation purposes
// ==== Drag'n'drop support ====
@Override
- public DropFeedback onDropEnter(final INode targetNode, final IDragElement[] elements) {
+ public DropFeedback onDropEnter(final INode targetNode, Object targetView,
+ final IDragElement[] elements) {
if (elements.length == 0) {
return null;
/** List of nodes which should have their weights cleared */
public List<INode> mClearWeights;
- private LinearResizeState(BaseLayoutRule rule, INode layout, INode node) {
- super(rule, layout, node);
+ private LinearResizeState(BaseLayoutRule rule, INode layout, Object layoutView,
+ INode node) {
+ super(rule, layout, layoutView, node);
unweightedSizes = mRulesEngine.measureChildren(layout,
new IClientRulesEngine.AttributeFilter() {
}
@Override
- protected ResizeState createResizeState(INode layout, INode node) {
- return new LinearResizeState(this, layout, node);
+ protected ResizeState createResizeState(INode layout, Object layoutView, INode node) {
+ return new LinearResizeState(this, layout, layoutView, node);
}
protected void updateResizeState(LinearResizeState resizeState, final INode node, INode layout,
import com.android.ide.common.api.INodeHandler;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.SegmentType;
import com.android.ide.common.layout.relative.ConstraintPainter;
import com.android.ide.common.layout.relative.GuidelinePainter;
@Override
public void paintSelectionFeedback(IGraphics graphics, INode parentNode,
- List<? extends INode> childNodes) {
- super.paintSelectionFeedback(graphics, parentNode, childNodes);
+ List<? extends INode> childNodes, Object view) {
+ super.paintSelectionFeedback(graphics, parentNode, childNodes, view);
boolean showDependents = true;
if (sShowStructure) {
// ==== Drag'n'drop support ====
@Override
- public DropFeedback onDropEnter(INode targetNode, final IDragElement[] elements) {
+ public DropFeedback onDropEnter(INode targetNode, Object targetView, IDragElement[] elements) {
return new DropFeedback(new MoveHandler(targetNode, elements, mRulesEngine),
new GuidelinePainter());
}
@Override
public DropFeedback onResizeBegin(INode child, INode parent,
- SegmentType horizontalEdgeType, SegmentType verticalEdgeType) {
+ SegmentType horizontalEdgeType, SegmentType verticalEdgeType,
+ Object childView, Object parentView) {
ResizeHandler state = new ResizeHandler(parent, child, mRulesEngine,
horizontalEdgeType, verticalEdgeType);
return new DropFeedback(state, new GuidelinePainter());
public int modifierMask;
/**
+ * The actual view object for the layout containing the resizing operation,
+ * or null if not known
+ */
+ public Object layoutView;
+
+ /**
* Constructs a new {@link ResizeState}
*
* @param rule the associated rule
* @param layout the parent layout containing the resized node
+ * @param layoutView the actual View instance for the layout, or null if not known
* @param node the node being resized
*/
- ResizeState(BaseLayoutRule rule, INode layout, INode node) {
+ ResizeState(BaseLayoutRule rule, INode layout, Object layoutView, INode node) {
mRule = rule;
this.layout = layout;
this.node = node;
+ this.layoutView = layoutView;
}
/**
@Override
public DropFeedback onResizeBegin(INode child, INode parent, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
+ SegmentType verticalEdge, Object childView, Object parentView) {
// Children of a table layout cannot set their widths (it is controlled by column
// settings on the table). They can set their heights (though for TableRow, the
// height is always wrap_content).
}
// Allow resizing heights only
- return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/);
+ return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/,
+ childView, parentView);
}
}
@Override
public DropFeedback onResizeBegin(INode child, INode parent, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
+ SegmentType verticalEdge, Object childView, Object parentView) {
// No resizing in TableRows; the width is *always* match_parent and the height is
// *always* wrap_content.
return null;
* Creates a new {@link GridDropHandler} for
* @param gridLayoutRule the corresponding {@link GridLayoutRule}
* @param layout the GridLayout node
+ * @param view the view instance of the grid layout receiving the drop
*/
- public GridDropHandler(GridLayoutRule gridLayoutRule, INode layout) {
+ public GridDropHandler(GridLayoutRule gridLayoutRule, INode layout, Object view) {
mRule = gridLayoutRule;
- mGrid = new GridModel(mRule.getRulesEngine(), layout);
+ mGrid = new GridModel(mRule.getRulesEngine(), layout, view);
}
/**
import com.android.ide.common.api.Rect;
import com.android.ide.common.api.SegmentType;
import com.android.ide.common.layout.GridLayoutRule;
+import com.android.util.Pair;
/**
* Painter which paints feedback during drag, drop and resizing operations, as well as
mRule.drawElement(gc, first, offsetX, offsetY);
}
}
+
+ /**
+ * Paints the structure (the row and column boundaries) of the given GridLayout
+ *
+ * @param view the instance of the GridLayout whose structure should be painted
+ * @param style the drawing style to use for the cell boundaries
+ * @param layout the layout element
+ * @param gc the graphics context
+ */
+ public static void paintStructure(Object view, DrawingStyle style, INode layout,
+ IGraphics gc) {
+ Pair<int[],int[]> cellBounds = GridModel.getAxisBounds(view);
+ if (cellBounds != null) {
+ int[] xs = cellBounds.getFirst();
+ int[] ys = cellBounds.getSecond();
+ Rect b = layout.getBounds();
+ gc.useStyle(style);
+ for (int row = 0; row < ys.length; row++) {
+ int y = ys[row] + b.y;
+ gc.drawLine(b.x, y, b.x2(), y);
+ }
+ for (int column = 0; column < xs.length; column++) {
+ int x = xs[column] + b.x;
+ gc.drawLine(x, b.y, x, b.y2());
+ }
+ }
+ }
}
import com.android.ide.common.api.Margins;
import com.android.ide.common.api.Rect;
import com.android.ide.common.layout.GridLayoutRule;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.util.Pair;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
private boolean stale;
/**
+ * An actual instance of a GridLayout object that this grid model corresponds to.
+ */
+ private Object mViewObject;
+
+ /**
* Constructs a {@link GridModel} for the given layout
*
* @param rulesEngine the associated rules engine
* @param node the GridLayout node
+ * @param viewObject an actual GridLayout instance, or null
*/
- public GridModel(IClientRulesEngine rulesEngine, INode node) {
+ public GridModel(IClientRulesEngine rulesEngine, INode node, Object viewObject) {
mRulesEngine = rulesEngine;
layout = node;
+ mViewObject = viewObject;
loadFromXml();
}
declaredRowCount == UNDEFINED ? children.length : declaredRowCount,
declaredColumnCount == UNDEFINED ? children.length : declaredColumnCount);
- // Compute the actualColumnCount and actualRowCount. This -should- be
- // as easy as declaredColumnCount + extraColumnsMap.size(),
- // but the user doesn't *have* to declare a column count (or a row count)
- // and we need both, so go and find the actual row and column maximums.
- int maxColumn = 0;
- int maxRow = 0;
- for (ViewData view : mChildViews) {
- maxColumn = max(maxColumn, view.column);
- maxRow = max(maxRow, view.row);
- }
- actualColumnCount = maxColumn + 1;
- actualRowCount = maxRow + 1;
-
assignCellBounds();
+
for (int i = 0; i <= actualRowCount; i++) {
mBaselines[i] = UNDEFINED;
}
}
/**
+ * Computes the positions of the column and row boundaries
+ */
+ private void assignCellBounds() {
+ if (!assignCellBoundsFromView()) {
+ assignCellBoundsFromBounds();
+ }
+ initializeMaxBounds();
+ mBaselines = new int[actualRowCount + 1];
+ }
+
+ /**
+ * Computes the positions of the column and row boundaries, using actual
+ * layout data from the associated GridLayout instance (stored in
+ * {@link #mViewObject})
+ */
+ private boolean assignCellBoundsFromView() {
+ if (mViewObject != null) {
+ Pair<int[], int[]> cellBounds = GridModel.getAxisBounds(mViewObject);
+ if (cellBounds != null) {
+ int[] xs = cellBounds.getFirst();
+ int[] ys = cellBounds.getSecond();
+
+ actualColumnCount = xs.length - 1;
+ actualRowCount = ys.length - 1;
+
+ Rect layoutBounds = layout.getBounds();
+ int layoutBoundsX = layoutBounds.x;
+ int layoutBoundsY = layoutBounds.y;
+ mLeft = new int[xs.length];
+ mTop = new int[ys.length];
+ for (int i = 0; i < xs.length; i++) {
+ mLeft[i] = xs[i] + layoutBoundsX;
+ }
+ for (int i = 0; i < ys.length; i++) {
+ mTop[i] = ys[i] + layoutBoundsY;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Computes the boundaries of the rows and columns by considering the bounds of the
* children.
*/
- private void assignCellBounds() {
+ private void assignCellBoundsFromBounds() {
Rect layoutBounds = layout.getBounds();
+
+ // Compute the actualColumnCount and actualRowCount. This -should- be
+ // as easy as declaredColumnCount + extraColumnsMap.size(),
+ // but the user doesn't *have* to declare a column count (or a row count)
+ // and we need both, so go and find the actual row and column maximums.
+ int maxColumn = 0;
+ int maxRow = 0;
+ for (ViewData view : mChildViews) {
+ maxColumn = max(maxColumn, view.column);
+ maxRow = max(maxRow, view.row);
+ }
+ actualColumnCount = maxColumn + 1;
+ actualRowCount = maxRow + 1;
+
mLeft = new int[actualColumnCount + 1];
- mMaxRight = new int[actualColumnCount + 1];
for (int i = 1; i < actualColumnCount; i++) {
mLeft[i] = UNDEFINED;
}
mLeft[0] = layoutBounds.x;
mLeft[actualColumnCount] = layoutBounds.x2();
mTop = new int[actualRowCount + 1];
- mMaxBottom = new int[actualRowCount + 1];
- mBaselines = new int[actualRowCount + 1];
for (int i = 1; i < actualRowCount; i++) {
mTop[i] = UNDEFINED;
}
} else {
mTop[row] = Math.min(bounds.y, mTop[row]);
}
-
- if (!view.isSpacer()) {
- int x2 = bounds.x2();
- int y2 = bounds.y2();
- int targetColumn = min(actualColumnCount - 1, column + view.columnSpan - 1);
- int targetRow = min(actualRowCount - 1, row + view.rowSpan - 1);
- IViewMetadata metadata = mRulesEngine.getMetadata(view.node.getFqcn());
- if (metadata != null) {
- Margins insets = metadata.getInsets();
- if (insets != null) {
- x2 -= insets.right;
- y2 -= insets.bottom;
- }
- }
- if (mMaxRight[targetColumn] < x2) {
- mMaxRight[targetColumn] = x2;
- }
- if (mMaxBottom[targetRow] < y2) {
- mMaxBottom[targetRow] = y2;
- }
- }
}
// Ensure that any empty columns/rows have a valid boundary value; for now,
}
/**
+ * Determine, for each row and column, what the largest x and y edges are
+ * within that row or column. This is used to find a natural split point to
+ * suggest when adding something "to the right of" or "below" another view.
+ */
+ private void initializeMaxBounds() {
+ mMaxRight = new int[actualColumnCount + 1];
+ mMaxBottom = new int[actualRowCount + 1];
+
+ for (ViewData view : mChildViews) {
+ Rect bounds = view.node.getBounds();
+ if (!bounds.isValid()) {
+ continue;
+ }
+
+ if (!view.isSpacer()) {
+ int x2 = bounds.x2();
+ int y2 = bounds.y2();
+ int column = view.column;
+ int row = view.row;
+ int targetColumn = min(actualColumnCount - 1,
+ column + view.columnSpan - 1);
+ int targetRow = min(actualRowCount - 1, row + view.rowSpan - 1);
+ IViewMetadata metadata = mRulesEngine.getMetadata(view.node.getFqcn());
+ if (metadata != null) {
+ Margins insets = metadata.getInsets();
+ if (insets != null) {
+ x2 -= insets.right;
+ y2 -= insets.bottom;
+ }
+ }
+ if (mMaxRight[targetColumn] < x2) {
+ mMaxRight[targetColumn] = x2;
+ }
+ if (mMaxBottom[targetRow] < y2) {
+ mMaxBottom[targetRow] = y2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Looks up the x[] and y[] locations of the columns and rows in the given GridLayout
+ * instance.
+ *
+ * @param view the GridLayout object, which should already have performed layout
+ * @return a pair of x[] and y[] integer arrays, or null if it could not be found
+ */
+ public static Pair<int[], int[]> getAxisBounds(Object view) {
+ try {
+ Class<?> clz = view.getClass();
+ Field horizontalAxis = clz.getDeclaredField("mHorizontalAxis"); //$NON-NLS-1$
+ Field verticalAxis = clz.getDeclaredField("mVerticalAxis"); //$NON-NLS-1$
+ horizontalAxis.setAccessible(true);
+ verticalAxis.setAccessible(true);
+ Object horizontal = horizontalAxis.get(view);
+ Object vertical = verticalAxis.get(view);
+ Field locations = horizontal.getClass().getDeclaredField("locations"); //$NON-NLS-1$
+ assert locations.getType().isArray() : locations.getType();
+ locations.setAccessible(true);
+ Object horizontalLocations = locations.get(horizontal);
+ Object verticalLocations = locations.get(vertical);
+ int[] xs = (int[]) horizontalLocations;
+ int[] ys = (int[]) verticalLocations;
+ return Pair.of(xs, ys);
+ } catch (Throwable t) {
+ AdtPlugin.log(t, null); // TODO: Add to API!
+ }
+
+ return null;
+ }
+
+ /**
* Add a new column.
*
* @param selectedChildren if null or empty, add the column at the end of the grid,
NodeProxy targetNode = mCanvas.getNodeFactory().create(target);
- mCanvas.getRulesEngine().callOnPaste(targetNode, pasted);
+ mCanvas.getRulesEngine().callOnPaste(targetNode, target.getViewObject(), pasted);
}
/**
targetVi = targetVi.getParent()) {
targetNode = mCanvas.getNodeFactory().create(targetVi);
df = mCanvas.getRulesEngine().callOnDropEnter(targetNode,
- mCurrentDragElements);
+ targetVi.getViewObject(), mCurrentDragElements);
if (df != null) {
// We should also dispatch an onDropMove() call to the initial enter
RulesEngine rulesEngine = mCanvas.getRulesEngine();
Rect newBounds = getNewBounds(pos);
+ ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
+ CanvasViewInfo childInfo = viewHierarchy.findViewInfoFor(mChildNode);
+ CanvasViewInfo parentInfo = viewHierarchy.findViewInfoFor(mParentNode);
+ Object childView = childInfo != null ? childInfo.getViewObject() : null;
+ Object parentView = parentInfo != null ? parentInfo.getViewObject() : null;
mFeedback = rulesEngine.callOnResizeBegin(mChildNode, mParentNode, newBounds,
- mHorizontalEdge, mVerticalEdge);
+ mHorizontalEdge, mVerticalEdge, childView, parentView);
update(pos);
mCanvas.getGestureManager().updateMessage(mFeedback);
}
if (root != null) {
NodeProxy parent = mCanvas.getNodeFactory().create(root);
rulesEngine.callPaintSelectionFeedback(gcWrapper,
- parent, Collections.<INode>emptyList());
+ parent, Collections.<INode>emptyList(), root.getViewObject());
}
}
if (root != null) {
NodeProxy parent = mCanvas.getNodeFactory().create(root);
rulesEngine.callPaintSelectionFeedback(gcWrapper,
- parent, Collections.<INode>emptyList());
+ parent, Collections.<INode>emptyList(), root.getViewObject());
}
}
}
parents.add(parentNode);
}
}
+ ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
for (INode parent : parents) {
List<INode> children = new ArrayList<INode>();
for (INode node : nodes) {
children.add(node);
}
}
+ CanvasViewInfo viewInfo = viewHierarchy.findViewInfoFor((NodeProxy) parent);
+ Object view = viewInfo != null ? viewInfo.getViewObject() : null;
+
rulesEngine.callPaintSelectionFeedback(gcWrapper,
- (NodeProxy) parent, children);
+ (NodeProxy) parent, children, view);
}
}
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.SegmentType;
import com.android.ide.common.layout.ViewRule;
import com.android.ide.eclipse.adt.AdtPlugin;
}
public void callPaintSelectionFeedback(GCWrapper gcWrapper, NodeProxy parentNode,
- List<? extends INode> childNodes) {
+ List<? extends INode> childNodes, Object view) {
// try to find a rule for this element's FQCN
IViewRule rule = loadRule(parentNode.getNode());
if (rule != null) {
try {
- rule.paintSelectionFeedback(gcWrapper, parentNode, childNodes);
+ rule.paintSelectionFeedback(gcWrapper, parentNode, childNodes, view);
} catch (Exception e) {
AdtPlugin.log(e, "%s.callPaintSelectionFeedback() failed: %s",
* Followed by a paint.
*/
public DropFeedback callOnDropEnter(NodeProxy targetNode,
- IDragElement[] elements) {
+ Object targetView, IDragElement[] elements) {
// try to find a rule for this element's FQCN
IViewRule rule = loadRule(targetNode.getNode());
if (rule != null) {
try {
- return rule.onDropEnter(targetNode, elements);
+ return rule.onDropEnter(targetNode, targetView, elements);
} catch (Exception e) {
AdtPlugin.log(e, "%s.onDropEnter() failed: %s",
* Called when pasting elements in an existing document on the selected target.
*
* @param targetNode The first node selected.
+ * @param targetView The view object for the target node, or null if not known
* @param pastedElements The elements being pasted.
*/
- public void callOnPaste(NodeProxy targetNode, SimpleElement[] pastedElements) {
+ public void callOnPaste(NodeProxy targetNode, Object targetView,
+ SimpleElement[] pastedElements) {
// try to find a rule for this element's FQCN
IViewRule rule = loadRule(targetNode.getNode());
if (rule != null) {
try {
mInsertType = InsertType.PASTE;
- rule.onPaste(targetNode, pastedElements);
+ rule.onPaste(targetNode, targetView, pastedElements);
} catch (Exception e) {
AdtPlugin.log(e, "%s.onPaste() failed: %s",
// ---- Resize operations ----
public DropFeedback callOnResizeBegin(NodeProxy child, NodeProxy parent, Rect newBounds,
- SegmentType horizontalEdge, SegmentType verticalEdge) {
+ SegmentType horizontalEdge, SegmentType verticalEdge, Object childView,
+ Object parentView) {
IViewRule rule = loadRule(parent.getNode());
if (rule != null) {
try {
- return rule.onResizeBegin(child, parent, horizontalEdge, verticalEdge);
+ return rule.onResizeBegin(child, parent, horizontalEdge, verticalEdge,
+ childView, parentView);
} catch (Exception e) {
AdtPlugin.log(e, "%s.onResizeBegin() failed: %s", rule.getClass().getSimpleName(),
e.toString());
"android.widget.Button", dragBounds).id(draggedButtonId));
// Enter target
- DropFeedback feedback = rule.onDropEnter(targetNode, elements);
+ DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
assertNotNull(feedback.painter);
"android.widget.Button", dragBounds).id("@+id/Button01"));
// Enter target
- DropFeedback feedback = rule.onDropEnter(targetNode, elements);
+ DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
assertNotNull(feedback.painter);
ZoomControlsRule rule = new ZoomControlsRule();
// Enter target
- DropFeedback feedback = rule.onDropEnter(layout, elements);
+ DropFeedback feedback = rule.onDropEnter(layout, null/*targetView*/, elements);
// Zoom controls don't respond to drags
assertNull(feedback);
}
TestNode targetNode = TestNode.create("android.widget.GridLayout").id("@+id/GridLayout1")
.bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI, ATTR_COLUMN_COUNT, "3");
- GridModel model = new GridModel(null, targetNode);
+ GridModel model = new GridModel(null, targetNode, null);
assertEquals(3, model.declaredColumnCount);
assertEquals(1, model.actualColumnCount);
assertEquals(1, model.actualRowCount);
targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button3"));
targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button4"));
- model = new GridModel(null, targetNode);
+ model = new GridModel(null, targetNode, null);
assertEquals(3, model.declaredColumnCount);
assertEquals(3, model.actualColumnCount);
assertEquals(2, model.actualRowCount);
* @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(IGraphics graphics, INode parentNode,
- List<? extends INode> childNodes);
+ List<? extends INode> childNodes, 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.
+ * 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
*/
- DropFeedback onDropEnter(INode targetNode,
- IDragElement[] elements);
+ DropFeedback onDropEnter(INode targetNode, Object targetView, 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.
+ * 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)
*/
DropFeedback onDropMove(INode targetNode,
IDragElement[] elements,
* <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(INode targetNode,
IDragElement[] elements,
* <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
+ * {@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(INode targetNode,
IDragElement[] elements,
* 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(INode targetNode, IDragElement[] pastedElements);
+ void onPaste(INode targetNode, Object targetView, IDragElement[] pastedElements);
// ==== XML Creation ====
* @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.
*/
- DropFeedback onResizeBegin(INode child, INode parent,
- SegmentType horizEdge, SegmentType verticalEdge);
+ DropFeedback onResizeBegin(
+ INode child, INode parent,
+ SegmentType horizEdge, SegmentType verticalEdge,
+ Object childView, Object parentView);
/**
* Called by the IDE on the parent layout when a child widget is being resized. This