diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java | 839 |
1 files changed, 0 insertions, 839 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java deleted file mode 100644 index db08b1857..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java +++ /dev/null @@ -1,839 +0,0 @@ -/* - * Copyright (C) 2011 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.layout.relative; - -import static com.android.ide.common.api.MarginType.NO_MARGIN; -import static com.android.ide.common.api.MarginType.WITHOUT_MARGIN; -import static com.android.ide.common.api.MarginType.WITH_MARGIN; -import static com.android.ide.common.api.SegmentType.BASELINE; -import static com.android.ide.common.api.SegmentType.BOTTOM; -import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL; -import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL; -import static com.android.ide.common.api.SegmentType.LEFT; -import static com.android.ide.common.api.SegmentType.RIGHT; -import static com.android.ide.common.api.SegmentType.TOP; -import static com.android.ide.common.layout.BaseLayoutRule.getMaxMatchDistance; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_BELOW; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF; -import static com.android.SdkConstants.VALUE_N_DP; -import static com.android.SdkConstants.VALUE_TRUE; -import static com.android.ide.common.layout.relative.ConstraintType.ALIGN_BASELINE; - -import static java.lang.Math.abs; - -import com.android.SdkConstants; -import static com.android.SdkConstants.ANDROID_URI; -import com.android.ide.common.api.DropFeedback; -import com.android.ide.common.api.IClientRulesEngine; -import com.android.ide.common.api.INode; -import com.android.ide.common.api.Margins; -import com.android.ide.common.api.Rect; -import com.android.ide.common.api.Segment; -import com.android.ide.common.api.SegmentType; -import com.android.ide.common.layout.BaseLayoutRule; -import com.android.ide.common.layout.relative.DependencyGraph.Constraint; -import com.android.ide.common.layout.relative.DependencyGraph.ViewData; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - -/** - * The {@link GuidelineHandler} class keeps track of state related to a guideline operation - * like move and resize, and performs various constraint computations. - */ -public class GuidelineHandler { - /** - * A dependency graph for the relative layout recording constraint relationships - */ - protected DependencyGraph mDependencyGraph; - - /** The RelativeLayout we are moving/resizing within */ - public INode layout; - - /** The set of nodes being dragged (may be null) */ - protected Collection<INode> mDraggedNodes; - - /** The bounds of the primary child node being dragged */ - protected Rect mBounds; - - /** Whether the left edge is being moved/resized */ - protected boolean mMoveLeft; - - /** Whether the right edge is being moved/resized */ - protected boolean mMoveRight; - - /** Whether the top edge is being moved/resized */ - protected boolean mMoveTop; - - /** Whether the bottom edge is being moved/resized */ - protected boolean mMoveBottom; - - /** - * Whether the drop/move/resize position should be snapped (which can be turned off - * with a modifier key during the operation) - */ - protected boolean mSnap = true; - - /** - * The set of nodes which depend on the currently selected nodes, including - * transitively, through horizontal constraints (a "horizontal constraint" - * is a constraint between two horizontal edges) - */ - protected Set<INode> mHorizontalDeps; - - /** - * The set of nodes which depend on the currently selected nodes, including - * transitively, through vertical constraints (a "vertical constraint" - * is a constraint between two vertical edges) - */ - protected Set<INode> mVerticalDeps; - - /** The current list of constraints which result in a horizontal cycle (if applicable) */ - protected List<Constraint> mHorizontalCycle; - - /** The current list of constraints which result in a vertical cycle (if applicable) */ - protected List<Constraint> mVerticalCycle; - - /** - * All horizontal segments in the relative layout - top and bottom edges, baseline - * edges, and top and bottom edges offset by the applicable margins in each direction - */ - protected List<Segment> mHorizontalEdges; - - /** - * All vertical segments in the relative layout - left and right edges, and left and - * right edges offset by the applicable margins in each direction - */ - protected List<Segment> mVerticalEdges; - - /** - * All center vertical segments in the relative layout. These are kept separate since - * they only match other center edges. - */ - protected List<Segment> mCenterVertEdges; - - /** - * All center horizontal segments in the relative layout. These are kept separate - * since they only match other center edges. - */ - protected List<Segment> mCenterHorizEdges; - - /** - * Suggestions for horizontal matches. There could be more than one, but all matches - * will be equidistant from the current position (as well as in the same direction, - * which means that you can't have one match 5 pixels to the left and one match 5 - * pixels to the right since it would be impossible to snap to fit with both; you can - * however have multiple matches all 5 pixels to the left.) - * <p - * The best vertical match will be found in {@link #mCurrentTopMatch} or - * {@link #mCurrentBottomMatch}. - */ - protected List<Match> mHorizontalSuggestions; - - /** - * Suggestions for vertical matches. - * <p - * The best vertical match will be found in {@link #mCurrentLeftMatch} or - * {@link #mCurrentRightMatch}. - */ - protected List<Match> mVerticalSuggestions; - - /** - * The current match on the left edge, or null if no match or if the left edge is not - * being moved or resized. - */ - protected Match mCurrentLeftMatch; - - /** - * The current match on the top edge, or null if no match or if the top edge is not - * being moved or resized. - */ - protected Match mCurrentTopMatch; - - /** - * The current match on the right edge, or null if no match or if the right edge is - * not being moved or resized. - */ - protected Match mCurrentRightMatch; - - /** - * The current match on the bottom edge, or null if no match or if the bottom edge is - * not being moved or resized. - */ - protected Match mCurrentBottomMatch; - - /** - * The amount of margin to add to the top edge, or 0 - */ - protected int mTopMargin; - - /** - * The amount of margin to add to the bottom edge, or 0 - */ - protected int mBottomMargin; - - /** - * The amount of margin to add to the left edge, or 0 - */ - protected int mLeftMargin; - - /** - * The amount of margin to add to the right edge, or 0 - */ - protected int mRightMargin; - - /** - * The associated rules engine - */ - protected IClientRulesEngine mRulesEngine; - - /** - * Construct a new {@link GuidelineHandler} for the given relative layout. - * - * @param layout the RelativeLayout to handle - */ - GuidelineHandler(INode layout, IClientRulesEngine rulesEngine) { - this.layout = layout; - mRulesEngine = rulesEngine; - - mHorizontalEdges = new ArrayList<Segment>(); - mVerticalEdges = new ArrayList<Segment>(); - mCenterVertEdges = new ArrayList<Segment>(); - mCenterHorizEdges = new ArrayList<Segment>(); - mDependencyGraph = new DependencyGraph(layout); - } - - /** - * Returns true if the handler has any suggestions to offer - * - * @return true if the handler has any suggestions to offer - */ - public boolean haveSuggestions() { - return mCurrentLeftMatch != null || mCurrentTopMatch != null - || mCurrentRightMatch != null || mCurrentBottomMatch != null; - } - - /** - * Returns the closest match. - * - * @return the closest match, or null if nothing matched - */ - protected Match pickBestMatch(List<Match> matches) { - int alternatives = matches.size(); - if (alternatives == 0) { - return null; - } else if (alternatives == 1) { - Match match = matches.get(0); - return match; - } else { - assert alternatives > 1; - Collections.sort(matches, new MatchComparator()); - return matches.get(0); - } - } - - private boolean checkCycle(DropFeedback feedback, Match match, boolean vertical) { - if (match != null && match.cycle) { - for (INode node : mDraggedNodes) { - INode from = match.edge.node; - assert match.with.node == null || match.with.node == node; - INode to = node; - List<Constraint> path = mDependencyGraph.getPathTo(from, to, vertical); - if (path != null) { - if (vertical) { - mVerticalCycle = path; - } else { - mHorizontalCycle = path; - } - String desc = Constraint.describePath(path, - match.type.name, match.edge.id); - - feedback.errorMessage = "Constraint creates a cycle: " + desc; - return true; - } - } - } - - return false; - } - - /** - * Checks for any cycles in the dependencies - * - * @param feedback the drop feedback state - */ - public void checkCycles(DropFeedback feedback) { - // Deliberate short circuit evaluation -- only list the first cycle - feedback.errorMessage = null; - mHorizontalCycle = null; - mVerticalCycle = null; - - if (checkCycle(feedback, mCurrentTopMatch, true /* vertical */) - || checkCycle(feedback, mCurrentBottomMatch, true)) { - } - - if (checkCycle(feedback, mCurrentLeftMatch, false) - || checkCycle(feedback, mCurrentRightMatch, false)) { - } - } - - /** Records the matchable outside edges for the given node to the potential match list */ - protected void addBounds(INode node, String id, - boolean addHorizontal, boolean addVertical) { - Rect b = node.getBounds(); - Margins margins = node.getMargins(); - if (addHorizontal) { - if (margins.top != 0) { - mHorizontalEdges.add(new Segment(b.y, b.x, b.x2(), node, id, TOP, WITHOUT_MARGIN)); - mHorizontalEdges.add(new Segment(b.y - margins.top, b.x, b.x2(), node, id, - TOP, WITH_MARGIN)); - } else { - mHorizontalEdges.add(new Segment(b.y, b.x, b.x2(), node, id, TOP, NO_MARGIN)); - } - if (margins.bottom != 0) { - mHorizontalEdges.add(new Segment(b.y2(), b.x, b.x2(), node, id, BOTTOM, - WITHOUT_MARGIN)); - mHorizontalEdges.add(new Segment(b.y2() + margins.bottom, b.x, b.x2(), node, - id, BOTTOM, WITH_MARGIN)); - } else { - mHorizontalEdges.add(new Segment(b.y2(), b.x, b.x2(), node, id, - BOTTOM, NO_MARGIN)); - } - } - if (addVertical) { - if (margins.left != 0) { - mVerticalEdges.add(new Segment(b.x, b.y, b.y2(), node, id, LEFT, WITHOUT_MARGIN)); - mVerticalEdges.add(new Segment(b.x - margins.left, b.y, b.y2(), node, id, LEFT, - WITH_MARGIN)); - } else { - mVerticalEdges.add(new Segment(b.x, b.y, b.y2(), node, id, LEFT, NO_MARGIN)); - } - - if (margins.right != 0) { - mVerticalEdges.add(new Segment(b.x2(), b.y, b.y2(), node, id, - RIGHT, WITHOUT_MARGIN)); - mVerticalEdges.add(new Segment(b.x2() + margins.right, b.y, b.y2(), node, id, - RIGHT, WITH_MARGIN)); - } else { - mVerticalEdges.add(new Segment(b.x2(), b.y, b.y2(), node, id, - RIGHT, NO_MARGIN)); - } - } - } - - /** Records the center edges for the given node to the potential match list */ - protected void addCenter(INode node, String id, - boolean addHorizontal, boolean addVertical) { - Rect b = node.getBounds(); - - if (addHorizontal) { - mCenterHorizEdges.add(new Segment(b.centerY(), b.x, b.x2(), - node, id, CENTER_HORIZONTAL, NO_MARGIN)); - } - if (addVertical) { - mCenterVertEdges.add(new Segment(b.centerX(), b.y, b.y2(), - node, id, CENTER_VERTICAL, NO_MARGIN)); - } - } - - /** Records the baseline edge for the given node to the potential match list */ - protected int addBaseLine(INode node, String id) { - int baselineY = node.getBaseline(); - if (baselineY != -1) { - Rect b = node.getBounds(); - mHorizontalEdges.add(new Segment(b.y + baselineY, b.x, b.x2(), node, id, BASELINE, - NO_MARGIN)); - } - - return baselineY; - } - - protected void snapVertical(Segment vEdge, int x, Rect newBounds) { - newBounds.x = x; - } - - protected void snapHorizontal(Segment hEdge, int y, Rect newBounds) { - newBounds.y = y; - } - - /** - * Returns whether two edge types are compatible. For example, we only match the - * center of one object with the center of another. - * - * @param edge the first edge type to compare - * @param dragged the second edge type to compare the first one with - * @param delta the delta between the two edge locations - * @return true if the two edge types can be compatibly matched - */ - protected boolean isEdgeTypeCompatible(SegmentType edge, SegmentType dragged, int delta) { - - if (Math.abs(delta) > BaseLayoutRule.getMaxMatchDistance()) { - if (dragged == LEFT || dragged == TOP) { - if (delta > 0) { - return false; - } - } else { - if (delta < 0) { - return false; - } - } - } - - switch (edge) { - case BOTTOM: - case TOP: - return dragged == TOP || dragged == BOTTOM; - case LEFT: - case RIGHT: - return dragged == LEFT || dragged == RIGHT; - - // Center horizontal, center vertical and Baseline only matches the same - // type, and only within the matching distance -- no margins! - case BASELINE: - case CENTER_HORIZONTAL: - case CENTER_VERTICAL: - return dragged == edge && Math.abs(delta) < getMaxMatchDistance(); - default: assert false : edge; - } - return false; - } - - /** - * Finds the closest matching segments among the given list of edges for the given - * dragged edge, and returns these as a list of matches - */ - protected List<Match> findClosest(Segment draggedEdge, List<Segment> edges) { - List<Match> closest = new ArrayList<Match>(); - addClosest(draggedEdge, edges, closest); - return closest; - } - - protected void addClosest(Segment draggedEdge, List<Segment> edges, - List<Match> closest) { - int at = draggedEdge.at; - int closestDelta = closest.size() > 0 ? closest.get(0).delta : Integer.MAX_VALUE; - int closestDistance = abs(closestDelta); - for (Segment edge : edges) { - assert draggedEdge.edgeType.isHorizontal() == edge.edgeType.isHorizontal(); - - int delta = edge.at - at; - int distance = abs(delta); - if (distance > closestDistance) { - continue; - } - - if (!isEdgeTypeCompatible(edge.edgeType, draggedEdge.edgeType, delta)) { - continue; - } - - boolean withParent = edge.node == layout; - ConstraintType type = ConstraintType.forMatch(withParent, - draggedEdge.edgeType, edge.edgeType); - if (type == null) { - continue; - } - - // Ensure that the edge match is compatible; for example, a "below" - // constraint can only apply to the margin bounds and a "bottom" - // constraint can only apply to the non-margin bounds. - if (type.relativeToMargin && edge.marginType == WITHOUT_MARGIN) { - continue; - } else if (!type.relativeToMargin && edge.marginType == WITH_MARGIN) { - continue; - } - - Match match = new Match(this, edge, draggedEdge, type, delta); - - if (distance < closestDistance) { - closest.clear(); - closestDistance = distance; - closestDelta = delta; - } else if (delta * closestDelta < 0) { - // They have different signs, e.g. the matches are equal but - // on opposite sides; can't accept them both - continue; - } - closest.add(match); - } - } - - protected void clearSuggestions() { - mHorizontalSuggestions = mVerticalSuggestions = null; - mCurrentLeftMatch = mCurrentRightMatch = null; - mCurrentTopMatch = mCurrentBottomMatch = null; - } - - /** - * Given a node, apply the suggestions by expressing them as relative layout param - * values - * - * @param n the node to apply constraints to - */ - public void applyConstraints(INode n) { - // Process each edge separately - String centerBoth = n.getStringAttr(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT); - if (centerBoth != null && centerBoth.equals(VALUE_TRUE)) { - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT, null); - - // If you had a center-in-both-directions attribute, and you're - // only resizing in one dimension, then leave the other dimension - // centered, e.g. if you have centerInParent and apply alignLeft, - // then you should end up with alignLeft and centerVertically - if (mCurrentTopMatch == null && mCurrentBottomMatch == null) { - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, VALUE_TRUE); - } - if (mCurrentLeftMatch == null && mCurrentRightMatch == null) { - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, VALUE_TRUE); - } - } - - if (mMoveTop) { - // Remove top attachments - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_TOP, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_TOP, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_BELOW, null); - - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BASELINE, null); - - } - - if (mMoveBottom) { - // Remove bottom attachments - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BOTTOM, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ABOVE, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BASELINE, null); - } - - if (mMoveLeft) { - // Remove left attachments - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_LEFT, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_LEFT, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_RIGHT_OF, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null); - } - - if (mMoveRight) { - // Remove right attachments - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_RIGHT, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_RIGHT, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_LEFT_OF, null); - n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null); - } - - if (mMoveTop && mCurrentTopMatch != null) { - applyConstraint(n, mCurrentTopMatch.getConstraint(true /* generateId */)); - if (mCurrentTopMatch.type == ALIGN_BASELINE) { - // HACK! WORKAROUND! Baseline doesn't provide a new bottom edge for attachments - String c = mCurrentTopMatch.getConstraint(true); - c = c.replace(ATTR_LAYOUT_ALIGN_BASELINE, ATTR_LAYOUT_ALIGN_BOTTOM); - applyConstraint(n, c); - } - } - - if (mMoveBottom && mCurrentBottomMatch != null) { - applyConstraint(n, mCurrentBottomMatch.getConstraint(true)); - } - - if (mMoveLeft && mCurrentLeftMatch != null) { - applyConstraint(n, mCurrentLeftMatch.getConstraint(true)); - } - - if (mMoveRight && mCurrentRightMatch != null) { - applyConstraint(n, mCurrentRightMatch.getConstraint(true)); - } - - if (mMoveLeft) { - applyMargin(n, ATTR_LAYOUT_MARGIN_LEFT, mLeftMargin); - } - if (mMoveRight) { - applyMargin(n, ATTR_LAYOUT_MARGIN_RIGHT, mRightMargin); - } - if (mMoveTop) { - applyMargin(n, ATTR_LAYOUT_MARGIN_TOP, mTopMargin); - } - if (mMoveBottom) { - applyMargin(n, ATTR_LAYOUT_MARGIN_BOTTOM, mBottomMargin); - } - } - - private void applyConstraint(INode n, String constraint) { - assert constraint.contains("=") : constraint; - String name = constraint.substring(0, constraint.indexOf('=')); - String value = constraint.substring(constraint.indexOf('=') + 1); - n.setAttribute(ANDROID_URI, name, value); - } - - private void applyMargin(INode n, String marginAttribute, int margin) { - if (margin > 0) { - int dp = mRulesEngine.pxToDp(margin); - n.setAttribute(ANDROID_URI, marginAttribute, String.format(VALUE_N_DP, dp)); - } else if (n.getStringAttr(ANDROID_URI, marginAttribute) != null) { - // Clear out existing margin - n.setAttribute(ANDROID_URI, marginAttribute, null); - } - } - - private void removeRelativeParams(INode node) { - for (ConstraintType type : ConstraintType.values()) { - node.setAttribute(ANDROID_URI, type.name, null); - } - node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_LEFT, null); - node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_RIGHT, null); - node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_TOP, null); - node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_BOTTOM, null); - } - - /** - * Attach the new child to the previous node - * @param previous the previous child - * @param node the new child to attach it to - */ - public void attachPrevious(INode previous, INode node) { - removeRelativeParams(node); - - String id = previous.getStringAttr(ANDROID_URI, ATTR_ID); - if (id == null) { - return; - } - - if (mCurrentTopMatch != null || mCurrentBottomMatch != null) { - // Attaching the top: arrange below, and for bottom arrange above - node.setAttribute(ANDROID_URI, - mCurrentTopMatch != null ? ATTR_LAYOUT_BELOW : ATTR_LAYOUT_ABOVE, id); - // Apply same left/right constraints as the parent - if (mCurrentLeftMatch != null) { - applyConstraint(node, mCurrentLeftMatch.getConstraint(true)); - applyMargin(node, ATTR_LAYOUT_MARGIN_LEFT, mLeftMargin); - } else if (mCurrentRightMatch != null) { - applyConstraint(node, mCurrentRightMatch.getConstraint(true)); - applyMargin(node, ATTR_LAYOUT_MARGIN_RIGHT, mRightMargin); - } - } else if (mCurrentLeftMatch != null || mCurrentRightMatch != null) { - node.setAttribute(ANDROID_URI, - mCurrentLeftMatch != null ? ATTR_LAYOUT_TO_RIGHT_OF : ATTR_LAYOUT_TO_LEFT_OF, - id); - // Apply same top/bottom constraints as the parent - if (mCurrentTopMatch != null) { - applyConstraint(node, mCurrentTopMatch.getConstraint(true)); - applyMargin(node, ATTR_LAYOUT_MARGIN_TOP, mTopMargin); - } else if (mCurrentBottomMatch != null) { - applyConstraint(node, mCurrentBottomMatch.getConstraint(true)); - applyMargin(node, ATTR_LAYOUT_MARGIN_BOTTOM, mBottomMargin); - } - } else { - return; - } - } - - /** Breaks any cycles detected by the handler */ - public void removeCycles() { - if (mHorizontalCycle != null) { - removeCycles(mHorizontalDeps); - } - if (mVerticalCycle != null) { - removeCycles(mVerticalDeps); - } - } - - private void removeCycles(Set<INode> deps) { - for (INode node : mDraggedNodes) { - ViewData view = mDependencyGraph.getView(node); - if (view != null) { - for (Constraint constraint : view.dependedOnBy) { - // For now, remove ALL constraints pointing to this node in this orientation. - // Later refine this to be smarter. (We can't JUST remove the constraints - // identified in the cycle since there could be multiple.) - constraint.from.node.setAttribute(ANDROID_URI, constraint.type.name, null); - } - } - } - } - - /** - * Comparator used to sort matches such that the first match is the most desirable - * match (where we prefer attaching to parent bounds, we avoid matches that lead to a - * cycle, we prefer constraints on closer widgets rather than ones further away, and - * so on.) - * <p> - * There are a number of sorting criteria. One of them is the distance between the - * matched edges. We may end up with multiple matches that are the same distance. In - * that case we look at the orientation; on the left side, prefer left-oriented - * attachments, and on the right-side prefer right-oriented attachments. For example, - * consider the following scenario: - * - * <pre> - * +--------------------+-------------------------+ - * | Attached on left | | - * +--------------------+ | - * | | - * | +-----+ | - * | | A | | - * | +-----+ | - * | | - * | +-------------------------+ - * | | Attached on right | - * +--------------------+-------------------------+ - * </pre> - * - * Here, dragging the left edge should attach to the top left attached view, whereas - * in the following layout dragging the right edge would attach to the bottom view: - * - * <pre> - * +--------------------------+-------------------+ - * | Attached on left | | - * +--------------------------+ | - * | | - * | +-----+ | - * | | A | | - * | +-----+ | - * | | - * | +-------------------+ - * | | Attached on right | - * +--------------------------+-------------------+ - * - * </pre> - * - * </ul> - */ - private final class MatchComparator implements Comparator<Match> { - @Override - public int compare(Match m1, Match m2) { - // Always prefer matching parent bounds - int parent1 = m1.edge.node == layout ? -1 : 1; - int parent2 = m2.edge.node == layout ? -1 : 1; - // unless it's a center bound -- those should always get lowest priority since - // they overlap with other usually more interesting edges near the center of - // the layout. - if (m1.edge.edgeType == CENTER_HORIZONTAL - || m1.edge.edgeType == CENTER_VERTICAL) { - parent1 = 2; - } - if (m2.edge.edgeType == CENTER_HORIZONTAL - || m2.edge.edgeType == CENTER_VERTICAL) { - parent2 = 2; - } - if (parent1 != parent2) { - return parent1 - parent2; - } - - // Avoid matching edges that would lead to a cycle - if (m1.edge.edgeType.isHorizontal()) { - int cycle1 = mHorizontalDeps.contains(m1.edge.node) ? 1 : -1; - int cycle2 = mHorizontalDeps.contains(m2.edge.node) ? 1 : -1; - if (cycle1 != cycle2) { - return cycle1 - cycle2; - } - } else { - int cycle1 = mVerticalDeps.contains(m1.edge.node) ? 1 : -1; - int cycle2 = mVerticalDeps.contains(m2.edge.node) ? 1 : -1; - if (cycle1 != cycle2) { - return cycle1 - cycle2; - } - } - - // TODO: Sort by minimum depth -- do we have the depth anywhere? - - // Prefer nodes that are closer - int distance1, distance2; - if (m1.edge.to <= m1.with.from) { - distance1 = m1.with.from - m1.edge.to; - } else if (m1.edge.from >= m1.with.to) { - distance1 = m1.edge.from - m1.with.to; - } else { - // Some kind of overlap - not sure how to prioritize these yet... - distance1 = 0; - } - if (m2.edge.to <= m2.with.from) { - distance2 = m2.with.from - m2.edge.to; - } else if (m2.edge.from >= m2.with.to) { - distance2 = m2.edge.from - m2.with.to; - } else { - // Some kind of overlap - not sure how to prioritize these yet... - distance2 = 0; - } - - if (distance1 != distance2) { - return distance1 - distance2; - } - - // Prefer matching on baseline - int baseline1 = (m1.edge.edgeType == BASELINE) ? -1 : 1; - int baseline2 = (m2.edge.edgeType == BASELINE) ? -1 : 1; - if (baseline1 != baseline2) { - return baseline1 - baseline2; - } - - // Prefer matching top/left edges before matching bottom/right edges - int orientation1 = (m1.with.edgeType == LEFT || - m1.with.edgeType == TOP) ? -1 : 1; - int orientation2 = (m2.with.edgeType == LEFT || - m2.with.edgeType == TOP) ? -1 : 1; - if (orientation1 != orientation2) { - return orientation1 - orientation2; - } - - // Prefer opposite-matching over same-matching. - // In other words, if we have the choice of matching - // our left edge with another element's left edge, - // or matching our left edge with another element's right - // edge, prefer the right edge since that - // The two matches have identical distance; try to sort by - // orientation - int edgeType1 = (m1.edge.edgeType != m1.with.edgeType) ? -1 : 1; - int edgeType2 = (m2.edge.edgeType != m2.with.edgeType) ? -1 : 1; - if (edgeType1 != edgeType2) { - return edgeType1 - edgeType2; - } - - return 0; - } - } - - /** - * Returns the {@link IClientRulesEngine} IDE callback - * - * @return the {@link IClientRulesEngine} IDE callback, never null - */ - public IClientRulesEngine getRulesEngine() { - return mRulesEngine; - } -} |