Guideline.java

/*
 * Copyright (C) 2015 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 androidx.constraintlayout.core.widgets;

import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;

import androidx.constraintlayout.core.LinearSystem;
import androidx.constraintlayout.core.SolverVariable;

import java.util.HashMap;

/**
 * Guideline
 */
public class Guideline extends ConstraintWidget {
    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    public static final int RELATIVE_PERCENT = 0;
    public static final int RELATIVE_BEGIN = 1;
    public static final int RELATIVE_END = 2;
    public static final int RELATIVE_UNKNOWN = -1;

    protected float mRelativePercent = -1;
    protected int mRelativeBegin = -1;
    protected int mRelativeEnd = -1;
    protected boolean mGuidelineUseRtl = true;

    private ConstraintAnchor mAnchor = mTop;
    private int mOrientation = HORIZONTAL;
    private int mMinimumPosition = 0;
    private boolean mResolved;

    public Guideline() {
        mAnchors.clear();
        mAnchors.add(mAnchor);
        final int count = mListAnchors.length;
        for (int i = 0; i < count; i++) {
            mListAnchors[i] = mAnchor;
        }
    }

    @Override
    public void copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map) {
        super.copy(src, map);
        Guideline srcGuideline = (Guideline) src;
        mRelativePercent = srcGuideline.mRelativePercent;
        mRelativeBegin = srcGuideline.mRelativeBegin;
        mRelativeEnd = srcGuideline.mRelativeEnd;
        mGuidelineUseRtl = srcGuideline.mGuidelineUseRtl;
        setOrientation(srcGuideline.mOrientation);
    }

    @Override
    public boolean allowedInBarrier() {
        return true;
    }

    // @TODO: add description
    public int getRelativeBehaviour() {
        if (mRelativePercent != -1) {
            return RELATIVE_PERCENT;
        }
        if (mRelativeBegin != -1) {
            return RELATIVE_BEGIN;
        }
        if (mRelativeEnd != -1) {
            return RELATIVE_END;
        }
        return RELATIVE_UNKNOWN;
    }

    // @TODO: add description
    public void setOrientation(int orientation) {
        if (mOrientation == orientation) {
            return;
        }
        mOrientation = orientation;
        mAnchors.clear();
        if (mOrientation == VERTICAL) {
            mAnchor = mLeft;
        } else {
            mAnchor = mTop;
        }
        mAnchors.add(mAnchor);
        final int count = mListAnchors.length;
        for (int i = 0; i < count; i++) {
            mListAnchors[i] = mAnchor;
        }
    }

    public ConstraintAnchor getAnchor() {
        return mAnchor;
    }

    /**
     * Specify the xml type for the container
     */
    @Override
    public String getType() {
        return "Guideline";
    }

    /**
     * get the orientation VERTICAL or HORIZONTAL
     * @return orientation
     */
    public int getOrientation() {
        return mOrientation;
    }

    /**
     * set the minimum position
     * @param minimum
     */
    public void setMinimumPosition(int minimum) {
        mMinimumPosition = minimum;
    }

    /**
     * Get the Minimum Position
     * @return the Minimum Position
     */
    public int getMinimumPosition() {
        return mMinimumPosition;
    }

    @Override
    public ConstraintAnchor getAnchor(ConstraintAnchor.Type anchorType) {
        switch (anchorType) {
            case LEFT:
            case RIGHT: {
                if (mOrientation == VERTICAL) {
                    return mAnchor;
                }
            }
            break;
            case TOP:
            case BOTTOM: {
                if (mOrientation == HORIZONTAL) {
                    return mAnchor;
                }
            }
            break;
            case BASELINE:
            case CENTER:
            case CENTER_X:
            case CENTER_Y:
            case NONE:
                return null;
        }
        return null;
    }

    // @TODO: add description
    public void setGuidePercent(int value) {
        setGuidePercent(value / 100f);
    }

    // @TODO: add description
    public void setGuidePercent(float value) {
        if (value > -1) {
            mRelativePercent = value;
            mRelativeBegin = -1;
            mRelativeEnd = -1;
        }
    }

    // @TODO: add description
    public void setGuideBegin(int value) {
        if (value > -1) {
            mRelativePercent = -1;
            mRelativeBegin = value;
            mRelativeEnd = -1;
        }
    }

    // @TODO: add description
    public void setGuideEnd(int value) {
        if (value > -1) {
            mRelativePercent = -1;
            mRelativeBegin = -1;
            mRelativeEnd = value;
        }
    }

    public float getRelativePercent() {
        return mRelativePercent;
    }

    public int getRelativeBegin() {
        return mRelativeBegin;
    }

    public int getRelativeEnd() {
        return mRelativeEnd;
    }

    // @TODO: add description
    public void setFinalValue(int position) {
        if (LinearSystem.FULL_DEBUG) {
            System.out.println("*** SET FINAL GUIDELINE VALUE "
                    + position + " FOR " + getDebugName());
        }
        mAnchor.setFinalValue(position);
        mResolved = true;
    }

    @Override
    public boolean isResolvedHorizontally() {
        return mResolved;
    }

    @Override
    public boolean isResolvedVertically() {
        return mResolved;
    }

    @Override
    public void addToSolver(LinearSystem system, boolean optimize) {
        if (LinearSystem.FULL_DEBUG) {
            System.out.println("\n----------------------------------------------");
            System.out.println("-- adding " + getDebugName() + " to the solver");
            System.out.println("----------------------------------------------\n");
        }

        ConstraintWidgetContainer parent = (ConstraintWidgetContainer) getParent();
        if (parent == null) {
            return;
        }
        ConstraintAnchor begin = parent.getAnchor(ConstraintAnchor.Type.LEFT);
        ConstraintAnchor end = parent.getAnchor(ConstraintAnchor.Type.RIGHT);
        boolean parentWrapContent = mParent != null
                ? mParent.mListDimensionBehaviors[DIMENSION_HORIZONTAL] == WRAP_CONTENT : false;
        if (mOrientation == HORIZONTAL) {
            begin = parent.getAnchor(ConstraintAnchor.Type.TOP);
            end = parent.getAnchor(ConstraintAnchor.Type.BOTTOM);
            parentWrapContent = mParent != null
                    ? mParent.mListDimensionBehaviors[DIMENSION_VERTICAL] == WRAP_CONTENT : false;
        }
        if (mResolved && mAnchor.hasFinalValue()) {
            SolverVariable guide = system.createObjectVariable(mAnchor);
            if (LinearSystem.FULL_DEBUG) {
                System.out.println("*** SET FINAL POSITION FOR GUIDELINE "
                        + getDebugName() + " TO " + mAnchor.getFinalValue());
            }
            system.addEquality(guide, mAnchor.getFinalValue());
            if (mRelativeBegin != -1) {
                if (parentWrapContent) {
                    system.addGreaterThan(system.createObjectVariable(end), guide,
                            0, SolverVariable.STRENGTH_EQUALITY);
                }
            } else if (mRelativeEnd != -1) {
                if (parentWrapContent) {
                    SolverVariable parentRight = system.createObjectVariable(end);
                    system.addGreaterThan(guide, system.createObjectVariable(begin),
                            0, SolverVariable.STRENGTH_EQUALITY);
                    system.addGreaterThan(parentRight, guide, 0, SolverVariable.STRENGTH_EQUALITY);
                }
            }
            mResolved = false;
            return;
        }
        if (mRelativeBegin != -1) {
            SolverVariable guide = system.createObjectVariable(mAnchor);
            SolverVariable parentLeft = system.createObjectVariable(begin);
            system.addEquality(guide, parentLeft, mRelativeBegin, SolverVariable.STRENGTH_FIXED);
            if (parentWrapContent) {
                system.addGreaterThan(system.createObjectVariable(end),
                        guide, 0, SolverVariable.STRENGTH_EQUALITY);
            }
        } else if (mRelativeEnd != -1) {
            SolverVariable guide = system.createObjectVariable(mAnchor);
            SolverVariable parentRight = system.createObjectVariable(end);
            system.addEquality(guide, parentRight, -mRelativeEnd, SolverVariable.STRENGTH_FIXED);
            if (parentWrapContent) {
                system.addGreaterThan(guide, system.createObjectVariable(begin),
                        0, SolverVariable.STRENGTH_EQUALITY);
                system.addGreaterThan(parentRight, guide, 0, SolverVariable.STRENGTH_EQUALITY);
            }
        } else if (mRelativePercent != -1) {
            SolverVariable guide = system.createObjectVariable(mAnchor);
            SolverVariable parentRight = system.createObjectVariable(end);
            system.addConstraint(LinearSystem
                    .createRowDimensionPercent(system, guide, parentRight,
                            mRelativePercent));
        }
    }

    @Override
    public void updateFromSolver(LinearSystem system, boolean optimize) {
        if (getParent() == null) {
            return;
        }
        int value = system.getObjectVariableValue(mAnchor);
        if (mOrientation == VERTICAL) {
            setX(value);
            setY(0);
            setHeight(getParent().getHeight());
            setWidth(0);
        } else {
            setX(0);
            setY(value);
            setWidth(getParent().getWidth());
            setHeight(0);
        }
    }

    void inferRelativePercentPosition() {
        float percent = (getX() / (float) getParent().getWidth());
        if (mOrientation == HORIZONTAL) {
            percent = (getY() / (float) getParent().getHeight());
        }
        setGuidePercent(percent);
    }

    void inferRelativeBeginPosition() {
        int position = getX();
        if (mOrientation == HORIZONTAL) {
            position = getY();
        }
        setGuideBegin(position);
    }

    void inferRelativeEndPosition() {
        int position = getParent().getWidth() - getX();
        if (mOrientation == HORIZONTAL) {
            position = getParent().getHeight() - getY();
        }
        setGuideEnd(position);
    }

    // @TODO: add description
    public void cyclePosition() {
        if (mRelativeBegin != -1) {
            // cycle to percent-based position
            inferRelativePercentPosition();
        } else if (mRelativePercent != -1) {
            // cycle to end-based position
            inferRelativeEndPosition();
        } else if (mRelativeEnd != -1) {
            // cycle to begin-based position
            inferRelativeBeginPosition();
        }
    }

    public boolean isPercent() {
        return mRelativePercent != -1 && mRelativeBegin == -1 && mRelativeEnd == -1;
    }
}