blob: 7a971736c1e99d846447bbd6cf74aff676bd286b [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.intellij.android.designer.model.layout.relative;
import com.android.tools.idea.designer.Segment;
import com.intellij.android.designer.designSurface.feedbacks.TextFeedback;
import com.intellij.android.designer.model.layout.TextDirection;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import static com.android.SdkConstants.*;
/**
* A match is a potential pairing of two segments with a given {@link ConstraintType}.
*/
class Match {
/**
* the edge of the dragged node that is matched
*/
public final Segment with;
/**
* the "other" edge that the dragged edge is matched with
*/
public final Segment edge;
/**
* the signed distance between the matched edges
*/
public final int delta;
/**
* the type of constraint this is a match for
*/
public final ConstraintType type;
/**
* whether this {@link Match} results in a cycle
*/
public boolean cycle;
/**
* Create a new match.
*
* @param edge the "other" edge that the dragged edge is matched with
* @param with the edge of the dragged node that is matched
* @param type the type of constraint this is a match for
* @param delta the signed distance between the matched edges
*/
public Match(Segment edge, Segment with, ConstraintType type, int delta) {
this.edge = edge;
this.with = with;
this.type = type;
this.delta = delta;
}
/**
* Returns the XML constraint attribute value for this match
*
* @param generateId whether an id should be generated if one is missing
* @return the XML constraint attribute value for this match
*/
public String getConstraint(boolean generateId) {
if (type.targetParent) {
return type.name + '=' + VALUE_TRUE;
}
else {
String id = edge.id;
if (id == null || id.length() == -1) {
if (!generateId) {
// Placeholder to display for the user during dragging
id = "<generated>";
}
else {
// Must generate an id on the fly!
// See if it's been set by a different constraint we've already applied
// to this same node
if (edge.node != null) {
id = edge.node.ensureId();
}
}
}
return type.name + '=' + id;
}
}
@Override
public String toString() {
return "Match [type=" + type + ", delta=" + delta + ", edge=" + edge + "]";
}
/** Style to use when describing component names */
@SuppressWarnings("UseJBColor") // The designer canvas is not using light/dark themes; colors match Android theme rendering
private static final SimpleTextAttributes SNAP_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD,
new Color(60, 139, 186));
/**
* Describes this match in the given {@link TextFeedback}
*
* @param feedback the feedback to write the description into
* @param margin the number of pixels to use as a margin
* @param marginAttribute the name of the applicable margin attribute
*/
public void describe(TextFeedback feedback, int margin, String marginAttribute) {
// Display the constraint. Remove the @id/ and @+id/ prefixes to make the text
// shorter and easier to read. This doesn't use stripPrefix() because the id is
// usually not a prefix of the value (for example, 'layout_alignBottom=@+id/foo').
feedback.clear();
String constraint = getConstraint(false /* generateId */);
String description = constraint.replace(NEW_ID_PREFIX, "").replace(ID_PREFIX, "");
if (description.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) {
description = description.substring(ATTR_LAYOUT_RESOURCE_PREFIX.length());
}
// Instead of "alignParentLeft=true", just display "alignParentLeft"
if (description.endsWith("=true")) {
description = description.substring(0, description.length() - "=true".length());
}
feedback.append(description, SNAP_ATTRIBUTES);
if (margin > 0) {
feedback.append(String.format(", margin=%1$d dp", margin));
}
}
@Nullable
public String getRtlConstraint(TextDirection textDirection, boolean generateId) {
switch (type) {
case ALIGN_LEFT:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_ALIGN_LEFT, textDirection.getAttrLeft());
case LAYOUT_LEFT_OF:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_TO_LEFT_OF, textDirection.getAttrLeftOf());
case ALIGN_RIGHT:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_ALIGN_RIGHT, textDirection.getAttrRight());
case LAYOUT_RIGHT_OF:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_TO_RIGHT_OF, textDirection.getAttrRightOf());
case ALIGN_PARENT_LEFT:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_ALIGN_PARENT_LEFT, textDirection.getAttrAlignParentLeft());
case ALIGN_PARENT_RIGHT:
return replaceAttribute(getConstraint(generateId), ATTR_LAYOUT_ALIGN_PARENT_RIGHT, textDirection.getAttrAlignParentRight());
}
return null;
}
private static String replaceAttribute(String s, String oldName, String newName) {
assert s.startsWith(oldName) : s;
return newName + s.substring(oldName.length());
}
}