aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java1092
1 files changed, 0 insertions, 1092 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
deleted file mode 100644
index 610fe5d8b..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
+++ /dev/null
@@ -1,1092 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_BASELINE_ALIGNED;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.ATTR_WEIGHT_SUM;
-import static com.android.SdkConstants.VALUE_1;
-import static com.android.SdkConstants.VALUE_HORIZONTAL;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.SdkConstants.VALUE_ZERO_DP;
-import static com.android.ide.eclipse.adt.AdtUtils.formatFloatAttribute;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-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.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.eclipse.adt.AdtPlugin;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.LinearLayout and all its derived
- * classes.
- */
-public class LinearLayoutRule extends BaseLayoutRule {
- private static final String ACTION_ORIENTATION = "_orientation"; //$NON-NLS-1$
- private static final String ACTION_WEIGHT = "_weight"; //$NON-NLS-1$
- private static final String ACTION_DISTRIBUTE = "_distribute"; //$NON-NLS-1$
- private static final String ACTION_BASELINE = "_baseline"; //$NON-NLS-1$
- private static final String ACTION_CLEAR = "_clear"; //$NON-NLS-1$
- private static final String ACTION_DOMINATE = "_dominate"; //$NON-NLS-1$
-
- private static final URL ICON_HORIZONTAL =
- LinearLayoutRule.class.getResource("hlinear.png"); //$NON-NLS-1$
- private static final URL ICON_VERTICAL =
- LinearLayoutRule.class.getResource("vlinear.png"); //$NON-NLS-1$
- private static final URL ICON_WEIGHTS =
- LinearLayoutRule.class.getResource("weights.png"); //$NON-NLS-1$
- private static final URL ICON_DISTRIBUTE =
- LinearLayoutRule.class.getResource("distribute.png"); //$NON-NLS-1$
- private static final URL ICON_BASELINE =
- LinearLayoutRule.class.getResource("baseline.png"); //$NON-NLS-1$
- private static final URL ICON_CLEAR_WEIGHTS =
- LinearLayoutRule.class.getResource("clearweights.png"); //$NON-NLS-1$
- private static final URL ICON_DOMINATE =
- LinearLayoutRule.class.getResource("allweight.png"); //$NON-NLS-1$
-
- /**
- * Returns the current orientation, regardless of whether it has been defined in XML
- *
- * @param node The LinearLayout to look up the orientation for
- * @return "horizontal" or "vertical" depending on the current orientation of the
- * linear layout
- */
- private String getCurrentOrientation(final INode node) {
- String orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
- if (orientation == null || orientation.length() == 0) {
- orientation = VALUE_HORIZONTAL;
- }
- return orientation;
- }
-
- /**
- * Returns true if the given node represents a vertical linear layout.
- * @param node the node to check layout orientation for
- * @return true if the layout is in vertical mode, otherwise false
- */
- protected boolean isVertical(INode node) {
- // Horizontal is the default, so if no value is specified it is horizontal.
- return VALUE_VERTICAL.equals(node.getStringAttr(ANDROID_URI,
- ATTR_ORIENTATION));
- }
-
- /**
- * Returns true if this LinearLayout supports switching orientation.
- *
- * @return true if this layout supports orientations
- */
- protected boolean supportsOrientation() {
- return true;
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
- if (supportsOrientation()) {
- Choices action = RuleAction.createChoices(
- ACTION_ORIENTATION, "Orientation", //$NON-NLS-1$
- new PropertyCallback(Collections.singletonList(parentNode),
- "Change LinearLayout Orientation",
- ANDROID_URI, ATTR_ORIENTATION),
- Arrays.<String>asList("Set Horizontal Orientation","Set Vertical Orientation"),
- Arrays.<URL>asList(ICON_HORIZONTAL, ICON_VERTICAL),
- Arrays.<String>asList("horizontal", "vertical"),
- getCurrentOrientation(parentNode),
- null /* icon */,
- -10,
- false /* supportsMultipleNodes */
- );
- action.setRadio(true);
- actions.add(action);
- }
- if (!isVertical(parentNode)) {
- String current = parentNode.getStringAttr(ANDROID_URI, ATTR_BASELINE_ALIGNED);
- boolean isAligned = current == null || Boolean.valueOf(current);
- actions.add(RuleAction.createToggle(ACTION_BASELINE, "Toggle Baseline Alignment",
- isAligned,
- new PropertyCallback(Collections.singletonList(parentNode),
- "Change Baseline Alignment",
- ANDROID_URI, ATTR_BASELINE_ALIGNED), // TODO: Also set index?
- ICON_BASELINE, 38, false));
- }
-
- // Gravity
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createSeparator(35));
-
- // Margins
- actions.add(createMarginAction(parentNode, children));
-
- // Gravity
- actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
-
- // Weights
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Change Weight", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String id = action.getId();
- if (id.equals(ACTION_WEIGHT)) {
- String weight =
- children.get(0).getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight == null || weight.length() == 0) {
- weight = "0.0"; //$NON-NLS-1$
- }
- weight = mRulesEngine.displayInput("Enter Weight Value:", weight,
- null);
- if (weight != null) {
- if (weight.isEmpty()) {
- weight = null; // remove attribute
- }
- for (INode child : children) {
- child.setAttribute(ANDROID_URI,
- ATTR_LAYOUT_WEIGHT, weight);
- }
- }
- } else if (id.equals(ACTION_DISTRIBUTE)) {
- distributeWeights(parentNode, parentNode.getChildren());
- } else if (id.equals(ACTION_CLEAR)) {
- clearWeights(parentNode);
- } else if (id.equals(ACTION_CLEAR) || id.equals(ACTION_DOMINATE)) {
- clearWeights(parentNode);
- distributeWeights(parentNode,
- children.toArray(new INode[children.size()]));
- } else {
- assert id.equals(ACTION_BASELINE);
- }
- }
- });
- }
- };
- actions.add(RuleAction.createSeparator(50));
- actions.add(RuleAction.createAction(ACTION_DISTRIBUTE, "Distribute Weights Evenly",
- actionCallback, ICON_DISTRIBUTE, 60, false /*supportsMultipleNodes*/));
- actions.add(RuleAction.createAction(ACTION_DOMINATE, "Assign All Weight",
- actionCallback, ICON_DOMINATE, 70, false));
- actions.add(RuleAction.createAction(ACTION_WEIGHT, "Change Layout Weight",
- actionCallback, ICON_WEIGHTS, 80, false));
- actions.add(RuleAction.createAction(ACTION_CLEAR, "Clear All Weights",
- actionCallback, ICON_CLEAR_WEIGHTS, 90, false));
- }
- }
-
- private void distributeWeights(INode parentNode, INode[] targets) {
- // Any XML to get weight sum?
- String weightSum = parentNode.getStringAttr(ANDROID_URI,
- ATTR_WEIGHT_SUM);
- double sum = -1.0;
- if (weightSum != null) {
- // Distribute
- try {
- sum = Double.parseDouble(weightSum);
- } catch (NumberFormatException nfe) {
- // Just keep using the default
- }
- }
- int numTargets = targets.length;
- double share;
- if (sum <= 0.0) {
- // The sum will be computed from the children, so just
- // use arbitrary amount
- share = 1.0;
- } else {
- share = sum / numTargets;
- }
- String value = formatFloatAttribute((float) share);
- String sizeAttribute = isVertical(parentNode) ?
- ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : targets) {
- target.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
- // Also set the width/height to 0dp to ensure actual equal
- // size (without this, only the remaining space is
- // distributed)
- if (VALUE_WRAP_CONTENT.equals(target.getStringAttr(ANDROID_URI, sizeAttribute))) {
- target.setAttribute(ANDROID_URI, sizeAttribute, VALUE_ZERO_DP);
- }
- }
- }
-
- private void clearWeights(INode parentNode) {
- // Clear attributes
- String sizeAttribute = isVertical(parentNode)
- ? ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : parentNode.getChildren()) {
- target.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- String size = target.getStringAttr(ANDROID_URI, sizeAttribute);
- if (size != null && size.startsWith("0")) { //$NON-NLS-1$
- target.setAttribute(ANDROID_URI, sizeAttribute, VALUE_WRAP_CONTENT);
- }
- }
- }
-
- // ==== Drag'n'drop support ====
-
- @Override
- public DropFeedback onDropEnter(final @NonNull INode targetNode, @Nullable Object targetView,
- final @Nullable IDragElement[] elements) {
-
- if (elements.length == 0) {
- return null;
- }
-
- Rect bn = targetNode.getBounds();
- if (!bn.isValid()) {
- return null;
- }
-
- boolean isVertical = isVertical(targetNode);
-
- // Prepare a list of insertion points: X coords for horizontal, Y for
- // vertical.
- List<MatchPos> indexes = new ArrayList<MatchPos>();
-
- int last = isVertical ? bn.y : bn.x;
- int pos = 0;
- boolean lastDragged = false;
- int selfPos = -1;
- for (INode it : targetNode.getChildren()) {
- Rect bc = it.getBounds();
- if (bc.isValid()) {
- // First see if this node looks like it's the same as one of the
- // *dragged* bounds
- boolean isDragged = false;
- for (IDragElement element : elements) {
- // This tries to determine if an INode corresponds to an
- // IDragElement, by comparing their bounds.
- if (element.isSame(it)) {
- isDragged = true;
- break;
- }
- }
-
- // We don't want to insert drag positions before or after the
- // element that is itself being dragged. However, we -do- want
- // to insert a match position here, at the center, such that
- // when you drag near its current position we show a match right
- // where it's already positioned.
- if (isDragged) {
- int v = isVertical ? bc.y + (bc.h / 2) : bc.x + (bc.w / 2);
- selfPos = pos;
- indexes.add(new MatchPos(v, pos++));
- } else if (lastDragged) {
- // Even though we don't want to insert a match below, we
- // need to increment the index counter such that subsequent
- // lines know their correct index in the child list.
- pos++;
- } else {
- // Add an insertion point between the last point and the
- // start of this child
- int v = isVertical ? bc.y : bc.x;
- v = (last + v) / 2;
- indexes.add(new MatchPos(v, pos++));
- }
-
- last = isVertical ? (bc.y + bc.h) : (bc.x + bc.w);
- lastDragged = isDragged;
- } else {
- // We still have to count this position even if it has no bounds, or
- // subsequent children will be inserted at the wrong place
- pos++;
- }
- }
-
- // Finally add an insert position after all the children - unless of
- // course we happened to be dragging the last element
- if (!lastDragged) {
- int v = last + 1;
- indexes.add(new MatchPos(v, pos));
- }
-
- int posCount = targetNode.getChildren().length + 1;
- return new DropFeedback(new LinearDropData(indexes, posCount, isVertical, selfPos),
- new IFeedbackPainter() {
-
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- // Paint callback for the LinearLayout. This is called
- // by the canvas when a draw is needed.
- drawFeedback(gc, node, elements, feedback);
- }
- });
- }
-
- void drawFeedback(IGraphics gc, INode node, IDragElement[] elements, DropFeedback feedback) {
- Rect b = node.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Highlight the receiver
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
-
- gc.useStyle(DrawingStyle.DROP_ZONE);
-
- LinearDropData data = (LinearDropData) feedback.userData;
- boolean isVertical = data.isVertical();
- int selfPos = data.getSelfPos();
-
- for (MatchPos it : data.getIndexes()) {
- int i = it.getDistance();
- int pos = it.getPosition();
- // Don't show insert drop zones for "self"-index since that one goes
- // right through the center of the widget rather than in a sibling
- // position
- if (pos != selfPos) {
- if (isVertical) {
- // draw horizontal lines
- gc.drawLine(b.x, i, b.x + b.w, i);
- } else {
- // draw vertical lines
- gc.drawLine(i, b.y, i, b.y + b.h);
- }
- }
- }
-
- Integer currX = data.getCurrX();
- Integer currY = data.getCurrY();
-
- if (currX != null && currY != null) {
- gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
-
- int x = currX;
- int y = currY;
-
- Rect be = elements[0].getBounds();
-
- // Draw a clear line at the closest drop zone (unless we're over the
- // dragged element itself)
- if (data.getInsertPos() != selfPos || selfPos == -1) {
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- if (data.getWidth() != null) {
- int width = data.getWidth();
- int fromX = x - width / 2;
- int toX = x + width / 2;
- gc.drawLine(fromX, y, toX, y);
- } else if (data.getHeight() != null) {
- int height = data.getHeight();
- int fromY = y - height / 2;
- int toY = y + height / 2;
- gc.drawLine(x, fromY, x, toY);
- }
- }
-
- if (be.isValid()) {
- boolean isLast = data.isLastPosition();
-
- // At least the first element has a bound. Draw rectangles for
- // all dropped elements with valid bounds, offset at the drop
- // point.
- int offsetX;
- int offsetY;
- if (isVertical) {
- offsetX = b.x - be.x;
- offsetY = currY - be.y - (isLast ? 0 : (be.h / 2));
-
- } else {
- offsetX = currX - be.x - (isLast ? 0 : (be.w / 2));
- offsetY = b.y - be.y;
- }
-
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- for (IDragElement element : elements) {
- Rect bounds = element.getBounds();
- if (bounds.isValid() && (bounds.w > b.w || bounds.h > b.h) &&
- node.getChildren().length == 0) {
- // The bounds of the child does not fully fit inside the target.
- // Limit the bounds to the layout bounds (but only when there
- // are no children, since otherwise positioning around the existing
- // children gets difficult)
- final int px, py, pw, ph;
- if (bounds.w > b.w) {
- px = b.x;
- pw = b.w;
- } else {
- px = bounds.x + offsetX;
- pw = bounds.w;
- }
- if (bounds.h > b.h) {
- py = b.y;
- ph = b.h;
- } else {
- py = bounds.y + offsetY;
- ph = bounds.h;
- }
- Rect within = new Rect(px, py, pw, ph);
- gc.drawRect(within);
- } else {
- drawElement(gc, element, offsetX, offsetY);
- }
- }
- }
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return feedback;
- }
-
- LinearDropData data = (LinearDropData) feedback.userData;
- boolean isVertical = data.isVertical();
-
- int bestDist = Integer.MAX_VALUE;
- int bestIndex = Integer.MIN_VALUE;
- Integer bestPos = null;
-
- for (MatchPos index : data.getIndexes()) {
- int i = index.getDistance();
- int pos = index.getPosition();
- int dist = (isVertical ? p.y : p.x) - i;
- if (dist < 0)
- dist = -dist;
- if (dist < bestDist) {
- bestDist = dist;
- bestIndex = i;
- bestPos = pos;
- if (bestDist <= 0)
- break;
- }
- }
-
- if (bestIndex != Integer.MIN_VALUE) {
- Integer oldX = data.getCurrX();
- Integer oldY = data.getCurrY();
-
- if (isVertical) {
- data.setCurrX(b.x + b.w / 2);
- data.setCurrY(bestIndex);
- data.setWidth(b.w);
- data.setHeight(null);
- } else {
- data.setCurrX(bestIndex);
- data.setCurrY(b.y + b.h / 2);
- data.setWidth(null);
- data.setHeight(b.h);
- }
-
- data.setInsertPos(bestPos);
-
- feedback.requestPaint = !equals(oldX, data.getCurrX())
- || !equals(oldY, data.getCurrY());
- }
-
- return feedback;
- }
-
- private static boolean equals(Integer i1, Integer i2) {
- if (i1 == i2) {
- return true;
- } else if (i1 != null) {
- return i1.equals(i2);
- } else {
- // We know i2 != null
- return i2.equals(i1);
- }
- }
-
- @Override
- public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback) {
- // ignore
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- final @Nullable DropFeedback feedback, final @NonNull Point p) {
-
- LinearDropData data = (LinearDropData) feedback.userData;
- final int initialInsertPos = data.getInsertPos();
- insertAt(targetNode, elements, feedback.isCopy || !feedback.sameCanvas, initialInsertPos);
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- if (insertType == InsertType.MOVE_WITHIN) {
- // Don't adjust widths/heights/weights when just moving within a single
- // LinearLayout
- return;
- }
-
- // Attempt to set fill-properties on newly added views such that for example,
- // in a vertical layout, a text field defaults to filling horizontally, but not
- // vertically.
- String fqcn = node.getFqcn();
- IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
- if (metadata != null) {
- boolean vertical = isVertical(parent);
- FillPreference fill = metadata.getFillPreference();
- String fillParent = getFillParentValueName();
- if (fill.fillHorizontally(vertical)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- } else if (!vertical && fill == FillPreference.WIDTH_IN_VERTICAL) {
- // In a horizontal layout, make views that would fill horizontally in a
- // vertical layout have a non-zero weight instead. This will make the item
- // fill but only enough to allow other views to be shown as well.
- // (However, for drags within the same layout we do not touch
- // the weight, since it might already have been tweaked to a particular
- // value)
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, VALUE_1);
- }
- if (fill.fillVertically(vertical)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- }
- }
-
- // If you insert into a layout that already is using layout weights,
- // and all the layout weights are the same (nonzero) value, then use
- // the same weight for this new layout as well. Also duplicate the 0dip/0px/0dp
- // sizes, if used.
- boolean duplicateWeight = true;
- boolean duplicate0dip = true;
- String sameWeight = null;
- String sizeAttribute = isVertical(parent) ? ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : parent.getChildren()) {
- if (target == node) {
- continue;
- }
- String weight = target.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight == null || weight.length() == 0) {
- duplicateWeight = false;
- break;
- } else if (sameWeight != null && !sameWeight.equals(weight)) {
- duplicateWeight = false;
- } else {
- sameWeight = weight;
- }
- String size = target.getStringAttr(ANDROID_URI, sizeAttribute);
- if (size != null && !size.startsWith("0")) { //$NON-NLS-1$
- duplicate0dip = false;
- break;
- }
- }
- if (duplicateWeight && sameWeight != null) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, sameWeight);
- if (duplicate0dip) {
- node.setAttribute(ANDROID_URI, sizeAttribute, VALUE_ZERO_DP);
- }
- }
- }
-
- /** A possible match position */
- private static class MatchPos {
- /** The pixel distance */
- private int mDistance;
- /** The position among siblings */
- private int mPosition;
-
- public MatchPos(int distance, int position) {
- mDistance = distance;
- mPosition = position;
- }
-
- @Override
- public String toString() {
- return "MatchPos [distance=" + mDistance //$NON-NLS-1$
- + ", position=" + mPosition //$NON-NLS-1$
- + "]"; //$NON-NLS-1$
- }
-
- private int getDistance() {
- return mDistance;
- }
-
- private int getPosition() {
- return mPosition;
- }
- }
-
- private static class LinearDropData {
- /** Vertical layout? */
- private final boolean mVertical;
-
- /** Insert points (pixels + index) */
- private final List<MatchPos> mIndexes;
-
- /** Number of insert positions in the target node */
- private final int mNumPositions;
-
- /** Current marker X position */
- private Integer mCurrX;
-
- /** Current marker Y position */
- private Integer mCurrY;
-
- /** Position of the dragged element in this layout (or
- -1 if the dragged element is from elsewhere) */
- private final int mSelfPos;
-
- /** Current drop insert index (-1 for "at the end") */
- private int mInsertPos = -1;
-
- /** width of match line if it's a horizontal one */
- private Integer mWidth;
-
- /** height of match line if it's a vertical one */
- private Integer mHeight;
-
- public LinearDropData(List<MatchPos> indexes, int numPositions,
- boolean isVertical, int selfPos) {
- mIndexes = indexes;
- mNumPositions = numPositions;
- mVertical = isVertical;
- mSelfPos = selfPos;
- }
-
- @Override
- public String toString() {
- return "LinearDropData [currX=" + mCurrX //$NON-NLS-1$
- + ", currY=" + mCurrY //$NON-NLS-1$
- + ", height=" + mHeight //$NON-NLS-1$
- + ", indexes=" + mIndexes //$NON-NLS-1$
- + ", insertPos=" + mInsertPos //$NON-NLS-1$
- + ", isVertical=" + mVertical //$NON-NLS-1$
- + ", selfPos=" + mSelfPos //$NON-NLS-1$
- + ", width=" + mWidth //$NON-NLS-1$
- + "]"; //$NON-NLS-1$
- }
-
- private boolean isVertical() {
- return mVertical;
- }
-
- private void setCurrX(Integer currX) {
- mCurrX = currX;
- }
-
- private Integer getCurrX() {
- return mCurrX;
- }
-
- private void setCurrY(Integer currY) {
- mCurrY = currY;
- }
-
- private Integer getCurrY() {
- return mCurrY;
- }
-
- private int getSelfPos() {
- return mSelfPos;
- }
-
- private void setInsertPos(int insertPos) {
- mInsertPos = insertPos;
- }
-
- private int getInsertPos() {
- return mInsertPos;
- }
-
- private List<MatchPos> getIndexes() {
- return mIndexes;
- }
-
- private void setWidth(Integer width) {
- mWidth = width;
- }
-
- private Integer getWidth() {
- return mWidth;
- }
-
- private void setHeight(Integer height) {
- mHeight = height;
- }
-
- private Integer getHeight() {
- return mHeight;
- }
-
- /**
- * Returns true if we are inserting into the last position
- *
- * @return true if we are inserting into the last position
- */
- public boolean isLastPosition() {
- return mInsertPos == mNumPositions - 1;
- }
- }
-
- /** Custom resize state used during linear layout resizing */
- private class LinearResizeState extends ResizeState {
- /** Whether the node should be assigned a new weight */
- public boolean useWeight;
- /** Weight sum to be applied to the parent */
- private float mNewWeightSum;
- /** The weight to be set on the node (provided {@link #useWeight} is true) */
- private float mWeight;
- /** Map from nodes to preferred bounds of nodes where the weights have been cleared */
- public final Map<INode, Rect> unweightedSizes;
- /** Total required size required by the siblings <b>without</b> weights */
- public int totalLength;
- /** List of nodes which should have their weights cleared */
- public List<INode> mClearWeights;
-
- private LinearResizeState(BaseLayoutRule rule, INode layout, Object layoutView,
- INode node) {
- super(rule, layout, layoutView, node);
-
- unweightedSizes = mRulesEngine.measureChildren(layout,
- new IClientRulesEngine.AttributeFilter() {
- @Override
- public String getAttribute(@NonNull INode n, @Nullable String namespace,
- @NonNull String localName) {
- // Clear out layout weights; we need to measure the unweighted sizes
- // of the children
- if (ATTR_LAYOUT_WEIGHT.equals(localName)
- && SdkConstants.NS_RESOURCES.equals(namespace)) {
- return ""; //$NON-NLS-1$
- }
-
- return null;
- }
- });
-
- // Compute total required size required by the siblings *without* weights
- totalLength = 0;
- final boolean isVertical = isVertical(layout);
- for (Map.Entry<INode, Rect> entry : unweightedSizes.entrySet()) {
- Rect preferredSize = entry.getValue();
- if (isVertical) {
- totalLength += preferredSize.h;
- } else {
- totalLength += preferredSize.w;
- }
- }
- }
-
- /** Resets the computed state */
- void reset() {
- mNewWeightSum = -1;
- useWeight = false;
- mClearWeights = null;
- }
-
- /** Sets a weight to be applied to the node */
- void setWeight(float weight) {
- useWeight = true;
- mWeight = weight;
- }
-
- /** Sets a weight sum to be applied to the parent layout */
- void setWeightSum(float weightSum) {
- mNewWeightSum = weightSum;
- }
-
- /** Marks that the given node should be cleared when applying the new size */
- void clearWeight(INode n) {
- if (mClearWeights == null) {
- mClearWeights = new ArrayList<INode>();
- }
- mClearWeights.add(n);
- }
-
- /** Applies the state to the nodes */
- public void apply() {
- assert useWeight;
-
- String value = mWeight > 0 ? formatFloatAttribute(mWeight) : null;
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
-
- if (mClearWeights != null) {
- for (INode n : mClearWeights) {
- if (getWeight(n) > 0.0f) {
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- }
- }
- }
-
- if (mNewWeightSum > 0.0) {
- layout.setAttribute(ANDROID_URI, ATTR_WEIGHT_SUM,
- formatFloatAttribute(mNewWeightSum));
- }
- }
- }
-
- @Override
- 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,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
- // Update the resize state.
- // This method attempts to compute a new layout weight to be used in the direction
- // of the linear layout. If the superclass has already determined that we can snap to
- // a wrap_content or match_parent boundary, we prefer that. Otherwise, we attempt to
- // compute a layout weight - which can fail if the size is too big (not enough room),
- // or if the size is too small (smaller than the natural width of the node), and so on.
- // In that case this method just aborts, which will leave the resize state object
- // in such a state that it will call the superclass to resize instead, which will fall
- // back to device independent pixel sizing.
- resizeState.reset();
-
- if (oldBounds.equals(newBounds)) {
- return;
- }
-
- // If we're setting the width/height to wrap_content/match_parent in the dimension of the
- // linear layout, then just apply wrap_content and clear weights.
- boolean isVertical = isVertical(layout);
- if (!isVertical && verticalEdge != null) {
- if (resizeState.wrapWidth || resizeState.fillWidth) {
- resizeState.clearWeight(node);
- return;
- }
- if (newBounds.w == oldBounds.w) {
- return;
- }
- }
-
- if (isVertical && horizontalEdge != null) {
- if (resizeState.wrapHeight || resizeState.fillHeight) {
- resizeState.clearWeight(node);
- return;
- }
- if (newBounds.h == oldBounds.h) {
- return;
- }
- }
-
- // Compute weight sum
- float sum = getWeightSum(layout);
- if (sum <= 0.0f) {
- sum = 1.0f;
- resizeState.setWeightSum(sum);
- }
-
- // If the new size of the node is smaller than its preferred/wrap_content size,
- // then we cannot use weights to size it; switch to pixel-based sizing instead
- Map<INode, Rect> sizes = resizeState.unweightedSizes;
- Rect nodePreferredSize = sizes.get(node);
- if (nodePreferredSize != null) {
- if (horizontalEdge != null && newBounds.h < nodePreferredSize.h ||
- verticalEdge != null && newBounds.w < nodePreferredSize.w) {
- return;
- }
- }
-
- Rect layoutBounds = layout.getBounds();
- int remaining = (isVertical ? layoutBounds.h : layoutBounds.w) - resizeState.totalLength;
- Rect nodeBounds = sizes.get(node);
- if (nodeBounds == null) {
- return;
- }
-
- if (remaining > 0) {
- int missing = 0;
- if (isVertical) {
- if (newBounds.h > nodeBounds.h) {
- missing = newBounds.h - nodeBounds.h;
- } else if (newBounds.h > resizeState.wrapBounds.h) {
- // The weights concern how much space to ADD to the view.
- // What if we have resized it to a size *smaller* than its current
- // size without the weight delta? This can happen if you for example
- // have set a hardcoded size, such as 500dp, and then size it to some
- // smaller size.
- missing = newBounds.h - resizeState.wrapBounds.h;
- remaining += nodeBounds.h - resizeState.wrapBounds.h;
- resizeState.wrapHeight = true;
- }
- } else {
- if (newBounds.w > nodeBounds.w) {
- missing = newBounds.w - nodeBounds.w;
- } else if (newBounds.w > resizeState.wrapBounds.w) {
- missing = newBounds.w - resizeState.wrapBounds.w;
- remaining += nodeBounds.w - resizeState.wrapBounds.w;
- resizeState.wrapWidth = true;
- }
- }
- if (missing > 0) {
- // (weight / weightSum) * remaining = missing, so
- // weight = missing * weightSum / remaining
- float weight = missing * sum / remaining;
- resizeState.setWeight(weight);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Overridden in this layout in order to make resizing affect the layout_weight
- * attribute instead of the layout_width (for horizontal LinearLayouts) or
- * layout_height (for vertical LinearLayouts).
- */
- @Override
- protected void setNewSizeBounds(ResizeState state, final INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
- LinearResizeState resizeState = (LinearResizeState) state;
- updateResizeState(resizeState, node, layout, oldBounds, newBounds,
- horizontalEdge, verticalEdge);
-
- if (resizeState.useWeight) {
- resizeState.apply();
-
- // Handle resizing in the opposite dimension of the layout
- final boolean isVertical = isVertical(layout);
- if (!isVertical && horizontalEdge != null) {
- if (newBounds.h != oldBounds.h || resizeState.wrapHeight
- || resizeState.fillHeight) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
- resizeState.getHeightAttribute());
- }
- }
- if (isVertical && verticalEdge != null) {
- if (newBounds.w != oldBounds.w || resizeState.wrapWidth || resizeState.fillWidth) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
- resizeState.getWidthAttribute());
- }
- }
- } else {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- super.setNewSizeBounds(resizeState, node, layout, oldBounds, newBounds,
- horizontalEdge, verticalEdge);
- }
- }
-
- @Override
- protected String getResizeUpdateMessage(ResizeState state, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- LinearResizeState resizeState = (LinearResizeState) state;
- updateResizeState(resizeState, child, parent, child.getBounds(), newBounds,
- horizontalEdge, verticalEdge);
-
- if (resizeState.useWeight) {
- String weight = formatFloatAttribute(resizeState.mWeight);
- String dimension = String.format("weight %1$s", weight);
-
- String width;
- String height;
- if (isVertical(parent)) {
- width = resizeState.getWidthAttribute();
- height = dimension;
- } else {
- width = dimension;
- height = resizeState.getHeightAttribute();
- }
-
- if (horizontalEdge == null) {
- return width;
- } else if (verticalEdge == null) {
- return height;
- } else {
- // U+00D7: Unicode for multiplication sign
- return String.format("%s \u00D7 %s", width, height);
- }
- } else {
- return super.getResizeUpdateMessage(state, child, parent, newBounds,
- horizontalEdge, verticalEdge);
- }
- }
-
- /**
- * Returns the layout weight of of the given child of a LinearLayout, or 0.0 if it
- * does not define a weight
- */
- private static float getWeight(INode linearLayoutChild) {
- String weight = linearLayoutChild.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight != null && weight.length() > 0) {
- try {
- return Float.parseFloat(weight);
- } catch (NumberFormatException nfe) {
- AdtPlugin.log(nfe, "Invalid weight %1$s", weight);
- }
- }
-
- return 0.0f;
- }
-
- /**
- * Returns the sum of all the layout weights of the children in the given LinearLayout
- *
- * @param linearLayout the layout to compute the total sum for
- * @return the total sum of all the layout weights in the given layout
- */
- private static float getWeightSum(INode linearLayout) {
- String weightSum = linearLayout.getStringAttr(ANDROID_URI,
- ATTR_WEIGHT_SUM);
- float sum = -1.0f;
- if (weightSum != null) {
- // Distribute
- try {
- sum = Float.parseFloat(weightSum);
- return sum;
- } catch (NumberFormatException nfe) {
- // Just keep using the default
- }
- }
-
- return getSumOfWeights(linearLayout);
- }
-
- private static float getSumOfWeights(INode linearLayout) {
- float sum = 0.0f;
- for (INode child : linearLayout.getChildren()) {
- sum += getWeight(child);
- }
-
- return sum;
- }
-}