1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.constraintlayout.core.widgets;
17 
18 import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
19 
20 import androidx.constraintlayout.core.LinearSystem;
21 import androidx.constraintlayout.core.SolverVariable;
22 
23 import java.util.HashMap;
24 
25 /**
26  * Guideline
27  */
28 public class Guideline extends ConstraintWidget {
29     public static final int HORIZONTAL = 0;
30     public static final int VERTICAL = 1;
31 
32     public static final int RELATIVE_PERCENT = 0;
33     public static final int RELATIVE_BEGIN = 1;
34     public static final int RELATIVE_END = 2;
35     public static final int RELATIVE_UNKNOWN = -1;
36 
37     protected float mRelativePercent = -1;
38     protected int mRelativeBegin = -1;
39     protected int mRelativeEnd = -1;
40     protected boolean mGuidelineUseRtl = true;
41 
42     private ConstraintAnchor mAnchor = mTop;
43     private int mOrientation = HORIZONTAL;
44     private int mMinimumPosition = 0;
45     private boolean mResolved;
46 
Guideline()47     public Guideline() {
48         mAnchors.clear();
49         mAnchors.add(mAnchor);
50         final int count = mListAnchors.length;
51         for (int i = 0; i < count; i++) {
52             mListAnchors[i] = mAnchor;
53         }
54     }
55 
56     @Override
copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map)57     public void copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map) {
58         super.copy(src, map);
59         Guideline srcGuideline = (Guideline) src;
60         mRelativePercent = srcGuideline.mRelativePercent;
61         mRelativeBegin = srcGuideline.mRelativeBegin;
62         mRelativeEnd = srcGuideline.mRelativeEnd;
63         mGuidelineUseRtl = srcGuideline.mGuidelineUseRtl;
64         setOrientation(srcGuideline.mOrientation);
65     }
66 
67     @Override
allowedInBarrier()68     public boolean allowedInBarrier() {
69         return true;
70     }
71 
72     // @TODO: add description
getRelativeBehaviour()73     public int getRelativeBehaviour() {
74         if (mRelativePercent != -1) {
75             return RELATIVE_PERCENT;
76         }
77         if (mRelativeBegin != -1) {
78             return RELATIVE_BEGIN;
79         }
80         if (mRelativeEnd != -1) {
81             return RELATIVE_END;
82         }
83         return RELATIVE_UNKNOWN;
84     }
85 
86     // @TODO: add description
setOrientation(int orientation)87     public void setOrientation(int orientation) {
88         if (mOrientation == orientation) {
89             return;
90         }
91         mOrientation = orientation;
92         mAnchors.clear();
93         if (mOrientation == VERTICAL) {
94             mAnchor = mLeft;
95         } else {
96             mAnchor = mTop;
97         }
98         mAnchors.add(mAnchor);
99         final int count = mListAnchors.length;
100         for (int i = 0; i < count; i++) {
101             mListAnchors[i] = mAnchor;
102         }
103     }
104 
getAnchor()105     public ConstraintAnchor getAnchor() {
106         return mAnchor;
107     }
108 
109     /**
110      * Specify the xml type for the container
111      */
112     @Override
getType()113     public String getType() {
114         return "Guideline";
115     }
116 
117     /**
118      * get the orientation VERTICAL or HORIZONTAL
119      * @return orientation
120      */
getOrientation()121     public int getOrientation() {
122         return mOrientation;
123     }
124 
125     /**
126      * set the minimum position
127      * @param minimum
128      */
setMinimumPosition(int minimum)129     public void setMinimumPosition(int minimum) {
130         mMinimumPosition = minimum;
131     }
132 
133     /**
134      * Get the Minimum Position
135      * @return the Minimum Position
136      */
getMinimumPosition()137     public int getMinimumPosition() {
138         return mMinimumPosition;
139     }
140 
141     @Override
getAnchor(ConstraintAnchor.Type anchorType)142     public ConstraintAnchor getAnchor(ConstraintAnchor.Type anchorType) {
143         switch (anchorType) {
144             case LEFT:
145             case RIGHT: {
146                 if (mOrientation == VERTICAL) {
147                     return mAnchor;
148                 }
149             }
150             break;
151             case TOP:
152             case BOTTOM: {
153                 if (mOrientation == HORIZONTAL) {
154                     return mAnchor;
155                 }
156             }
157             break;
158             case BASELINE:
159             case CENTER:
160             case CENTER_X:
161             case CENTER_Y:
162             case NONE:
163                 return null;
164         }
165         return null;
166     }
167 
168     // @TODO: add description
setGuidePercent(int value)169     public void setGuidePercent(int value) {
170         setGuidePercent(value / 100f);
171     }
172 
173     // @TODO: add description
setGuidePercent(float value)174     public void setGuidePercent(float value) {
175         if (value > -1) {
176             mRelativePercent = value;
177             mRelativeBegin = -1;
178             mRelativeEnd = -1;
179         }
180     }
181 
182     // @TODO: add description
setGuideBegin(int value)183     public void setGuideBegin(int value) {
184         if (value > -1) {
185             mRelativePercent = -1;
186             mRelativeBegin = value;
187             mRelativeEnd = -1;
188         }
189     }
190 
191     // @TODO: add description
setGuideEnd(int value)192     public void setGuideEnd(int value) {
193         if (value > -1) {
194             mRelativePercent = -1;
195             mRelativeBegin = -1;
196             mRelativeEnd = value;
197         }
198     }
199 
getRelativePercent()200     public float getRelativePercent() {
201         return mRelativePercent;
202     }
203 
getRelativeBegin()204     public int getRelativeBegin() {
205         return mRelativeBegin;
206     }
207 
getRelativeEnd()208     public int getRelativeEnd() {
209         return mRelativeEnd;
210     }
211 
212     // @TODO: add description
setFinalValue(int position)213     public void setFinalValue(int position) {
214         if (LinearSystem.FULL_DEBUG) {
215             System.out.println("*** SET FINAL GUIDELINE VALUE "
216                     + position + " FOR " + getDebugName());
217         }
218         mAnchor.setFinalValue(position);
219         mResolved = true;
220     }
221 
222     @Override
isResolvedHorizontally()223     public boolean isResolvedHorizontally() {
224         return mResolved;
225     }
226 
227     @Override
isResolvedVertically()228     public boolean isResolvedVertically() {
229         return mResolved;
230     }
231 
232     @Override
addToSolver(LinearSystem system, boolean optimize)233     public void addToSolver(LinearSystem system, boolean optimize) {
234         if (LinearSystem.FULL_DEBUG) {
235             System.out.println("\n----------------------------------------------");
236             System.out.println("-- adding " + getDebugName() + " to the solver");
237             System.out.println("----------------------------------------------\n");
238         }
239 
240         ConstraintWidgetContainer parent = (ConstraintWidgetContainer) getParent();
241         if (parent == null) {
242             return;
243         }
244         ConstraintAnchor begin = parent.getAnchor(ConstraintAnchor.Type.LEFT);
245         ConstraintAnchor end = parent.getAnchor(ConstraintAnchor.Type.RIGHT);
246         boolean parentWrapContent = mParent != null
247                 ? mParent.mListDimensionBehaviors[DIMENSION_HORIZONTAL] == WRAP_CONTENT : false;
248         if (mOrientation == HORIZONTAL) {
249             begin = parent.getAnchor(ConstraintAnchor.Type.TOP);
250             end = parent.getAnchor(ConstraintAnchor.Type.BOTTOM);
251             parentWrapContent = mParent != null
252                     ? mParent.mListDimensionBehaviors[DIMENSION_VERTICAL] == WRAP_CONTENT : false;
253         }
254         if (mResolved && mAnchor.hasFinalValue()) {
255             SolverVariable guide = system.createObjectVariable(mAnchor);
256             if (LinearSystem.FULL_DEBUG) {
257                 System.out.println("*** SET FINAL POSITION FOR GUIDELINE "
258                         + getDebugName() + " TO " + mAnchor.getFinalValue());
259             }
260             system.addEquality(guide, mAnchor.getFinalValue());
261             if (mRelativeBegin != -1) {
262                 if (parentWrapContent) {
263                     system.addGreaterThan(system.createObjectVariable(end), guide,
264                             0, SolverVariable.STRENGTH_EQUALITY);
265                 }
266             } else if (mRelativeEnd != -1) {
267                 if (parentWrapContent) {
268                     SolverVariable parentRight = system.createObjectVariable(end);
269                     system.addGreaterThan(guide, system.createObjectVariable(begin),
270                             0, SolverVariable.STRENGTH_EQUALITY);
271                     system.addGreaterThan(parentRight, guide, 0, SolverVariable.STRENGTH_EQUALITY);
272                 }
273             }
274             mResolved = false;
275             return;
276         }
277         if (mRelativeBegin != -1) {
278             SolverVariable guide = system.createObjectVariable(mAnchor);
279             SolverVariable parentLeft = system.createObjectVariable(begin);
280             system.addEquality(guide, parentLeft, mRelativeBegin, SolverVariable.STRENGTH_FIXED);
281             if (parentWrapContent) {
282                 system.addGreaterThan(system.createObjectVariable(end),
283                         guide, 0, SolverVariable.STRENGTH_EQUALITY);
284             }
285         } else if (mRelativeEnd != -1) {
286             SolverVariable guide = system.createObjectVariable(mAnchor);
287             SolverVariable parentRight = system.createObjectVariable(end);
288             system.addEquality(guide, parentRight, -mRelativeEnd, SolverVariable.STRENGTH_FIXED);
289             if (parentWrapContent) {
290                 system.addGreaterThan(guide, system.createObjectVariable(begin),
291                         0, SolverVariable.STRENGTH_EQUALITY);
292                 system.addGreaterThan(parentRight, guide, 0, SolverVariable.STRENGTH_EQUALITY);
293             }
294         } else if (mRelativePercent != -1) {
295             SolverVariable guide = system.createObjectVariable(mAnchor);
296             SolverVariable parentRight = system.createObjectVariable(end);
297             system.addConstraint(LinearSystem
298                     .createRowDimensionPercent(system, guide, parentRight,
299                             mRelativePercent));
300         }
301     }
302 
303     @Override
updateFromSolver(LinearSystem system, boolean optimize)304     public void updateFromSolver(LinearSystem system, boolean optimize) {
305         if (getParent() == null) {
306             return;
307         }
308         int value = system.getObjectVariableValue(mAnchor);
309         if (mOrientation == VERTICAL) {
310             setX(value);
311             setY(0);
312             setHeight(getParent().getHeight());
313             setWidth(0);
314         } else {
315             setX(0);
316             setY(value);
317             setWidth(getParent().getWidth());
318             setHeight(0);
319         }
320     }
321 
inferRelativePercentPosition()322     void inferRelativePercentPosition() {
323         float percent = (getX() / (float) getParent().getWidth());
324         if (mOrientation == HORIZONTAL) {
325             percent = (getY() / (float) getParent().getHeight());
326         }
327         setGuidePercent(percent);
328     }
329 
inferRelativeBeginPosition()330     void inferRelativeBeginPosition() {
331         int position = getX();
332         if (mOrientation == HORIZONTAL) {
333             position = getY();
334         }
335         setGuideBegin(position);
336     }
337 
inferRelativeEndPosition()338     void inferRelativeEndPosition() {
339         int position = getParent().getWidth() - getX();
340         if (mOrientation == HORIZONTAL) {
341             position = getParent().getHeight() - getY();
342         }
343         setGuideEnd(position);
344     }
345 
346     // @TODO: add description
cyclePosition()347     public void cyclePosition() {
348         if (mRelativeBegin != -1) {
349             // cycle to percent-based position
350             inferRelativePercentPosition();
351         } else if (mRelativePercent != -1) {
352             // cycle to end-based position
353             inferRelativeEndPosition();
354         } else if (mRelativeEnd != -1) {
355             // cycle to begin-based position
356             inferRelativeBeginPosition();
357         }
358     }
359 
isPercent()360     public boolean isPercent() {
361         return mRelativePercent != -1 && mRelativeBegin == -1 && mRelativeEnd == -1;
362     }
363 }
364