1/*
2 * Copyright (C) 2006 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
17package android.view;
18import android.graphics.Rect;
19
20/**
21 * Standard constants and tools for placing an object within a potentially
22 * larger container.
23 */
24public class Gravity
25{
26    /** Constant indicating that no gravity has been set **/
27    public static final int NO_GRAVITY = 0x0000;
28
29    /** Raw bit indicating the gravity for an axis has been specified. */
30    public static final int AXIS_SPECIFIED = 0x0001;
31
32    /** Raw bit controlling how the left/top edge is placed. */
33    public static final int AXIS_PULL_BEFORE = 0x0002;
34    /** Raw bit controlling how the right/bottom edge is placed. */
35    public static final int AXIS_PULL_AFTER = 0x0004;
36    /** Raw bit controlling whether the right/bottom edge is clipped to its
37     * container, based on the gravity direction being applied. */
38    public static final int AXIS_CLIP = 0x0008;
39
40    /** Bits defining the horizontal axis. */
41    public static final int AXIS_X_SHIFT = 0;
42    /** Bits defining the vertical axis. */
43    public static final int AXIS_Y_SHIFT = 4;
44
45    /** Push object to the top of its container, not changing its size. */
46    public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
47    /** Push object to the bottom of its container, not changing its size. */
48    public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
49    /** Push object to the left of its container, not changing its size. */
50    public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
51    /** Push object to the right of its container, not changing its size. */
52    public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
53
54    /** Place object in the vertical center of its container, not changing its
55     *  size. */
56    public static final int CENTER_VERTICAL = AXIS_SPECIFIED<<AXIS_Y_SHIFT;
57    /** Grow the vertical size of the object if needed so it completely fills
58     *  its container. */
59    public static final int FILL_VERTICAL = TOP|BOTTOM;
60
61    /** Place object in the horizontal center of its container, not changing its
62     *  size. */
63    public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED<<AXIS_X_SHIFT;
64    /** Grow the horizontal size of the object if needed so it completely fills
65     *  its container. */
66    public static final int FILL_HORIZONTAL = LEFT|RIGHT;
67
68    /** Place the object in the center of its container in both the vertical
69     *  and horizontal axis, not changing its size. */
70    public static final int CENTER = CENTER_VERTICAL|CENTER_HORIZONTAL;
71
72    /** Grow the horizontal and vertical size of the object if needed so it
73     *  completely fills its container. */
74    public static final int FILL = FILL_VERTICAL|FILL_HORIZONTAL;
75
76    /** Flag to clip the edges of the object to its container along the
77     *  vertical axis. */
78    public static final int CLIP_VERTICAL = AXIS_CLIP<<AXIS_Y_SHIFT;
79
80    /** Flag to clip the edges of the object to its container along the
81     *  horizontal axis. */
82    public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
83
84    /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
85     * absolute LEFT/RIGHT).
86     */
87    public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
88
89    /**
90     * Binary mask to get the absolute horizontal gravity of a gravity.
91     */
92    public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
93            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
94    /**
95     * Binary mask to get the vertical gravity of a gravity.
96     */
97    public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
98            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
99
100    /** Special constant to enable clipping to an overall display along the
101     *  vertical dimension.  This is not applied by default by
102     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
103     *  yourself by calling {@link #applyDisplay}.
104     */
105    public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
106
107    /** Special constant to enable clipping to an overall display along the
108     *  horizontal dimension.  This is not applied by default by
109     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
110     *  yourself by calling {@link #applyDisplay}.
111     */
112    public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
113
114    /** Push object to x-axis position at the start of its container, not changing its size. */
115    public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
116
117    /** Push object to x-axis position at the end of its container, not changing its size. */
118    public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
119
120    /**
121     * Binary mask for the horizontal gravity and script specific direction bit.
122     */
123    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
124
125    /**
126     * Apply a gravity constant to an object. This suppose that the layout direction is LTR.
127     *
128     * @param gravity The desired placement of the object, as defined by the
129     *                constants in this class.
130     * @param w The horizontal size of the object.
131     * @param h The vertical size of the object.
132     * @param container The frame of the containing space, in which the object
133     *                  will be placed.  Should be large enough to contain the
134     *                  width and height of the object.
135     * @param outRect Receives the computed frame of the object in its
136     *                container.
137     */
138    public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
139        apply(gravity, w, h, container, 0, 0, outRect);
140    }
141
142    /**
143     * Apply a gravity constant to an object and take care if layout direction is RTL or not.
144     *
145     * @param gravity The desired placement of the object, as defined by the
146     *                constants in this class.
147     * @param w The horizontal size of the object.
148     * @param h The vertical size of the object.
149     * @param container The frame of the containing space, in which the object
150     *                  will be placed.  Should be large enough to contain the
151     *                  width and height of the object.
152     * @param outRect Receives the computed frame of the object in its
153     *                container.
154     * @param layoutDirection The layout direction.
155     *
156     * @see {@link View#LAYOUT_DIRECTION_LTR}
157     * @see {@link View#LAYOUT_DIRECTION_RTL}
158     * @hide
159     */
160    public static void apply(int gravity, int w, int h, Rect container,
161            Rect outRect, int layoutDirection) {
162        int absGravity = getAbsoluteGravity(gravity, layoutDirection);
163        apply(absGravity, w, h, container, 0, 0, outRect);
164    }
165
166    /**
167     * Apply a gravity constant to an object.
168     *
169     * @param gravity The desired placement of the object, as defined by the
170     *                constants in this class.
171     * @param w The horizontal size of the object.
172     * @param h The vertical size of the object.
173     * @param container The frame of the containing space, in which the object
174     *                  will be placed.  Should be large enough to contain the
175     *                  width and height of the object.
176     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
177     *             pushes it to the right; if gravity is RIGHT it pushes it to
178     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
179     *             right or left; otherwise it is ignored.
180     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
181     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
182     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
183     *             ignored.
184     * @param outRect Receives the computed frame of the object in its
185     *                container.
186     */
187    public static void apply(int gravity, int w, int h, Rect container,
188            int xAdj, int yAdj, Rect outRect) {
189        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
190            case 0:
191                outRect.left = container.left
192                        + ((container.right - container.left - w)/2) + xAdj;
193                outRect.right = outRect.left + w;
194                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
195                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
196                    if (outRect.left < container.left) {
197                        outRect.left = container.left;
198                    }
199                    if (outRect.right > container.right) {
200                        outRect.right = container.right;
201                    }
202                }
203                break;
204            case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
205                outRect.left = container.left + xAdj;
206                outRect.right = outRect.left + w;
207                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
208                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
209                    if (outRect.right > container.right) {
210                        outRect.right = container.right;
211                    }
212                }
213                break;
214            case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
215                outRect.right = container.right - xAdj;
216                outRect.left = outRect.right - w;
217                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
218                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
219                    if (outRect.left < container.left) {
220                        outRect.left = container.left;
221                    }
222                }
223                break;
224            default:
225                outRect.left = container.left + xAdj;
226                outRect.right = container.right + xAdj;
227                break;
228        }
229
230        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
231            case 0:
232                outRect.top = container.top
233                        + ((container.bottom - container.top - h)/2) + yAdj;
234                outRect.bottom = outRect.top + h;
235                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
236                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
237                    if (outRect.top < container.top) {
238                        outRect.top = container.top;
239                    }
240                    if (outRect.bottom > container.bottom) {
241                        outRect.bottom = container.bottom;
242                    }
243                }
244                break;
245            case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
246                outRect.top = container.top + yAdj;
247                outRect.bottom = outRect.top + h;
248                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
249                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
250                    if (outRect.bottom > container.bottom) {
251                        outRect.bottom = container.bottom;
252                    }
253                }
254                break;
255            case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
256                outRect.bottom = container.bottom - yAdj;
257                outRect.top = outRect.bottom - h;
258                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
259                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
260                    if (outRect.top < container.top) {
261                        outRect.top = container.top;
262                    }
263                }
264                break;
265            default:
266                outRect.top = container.top + yAdj;
267                outRect.bottom = container.bottom + yAdj;
268                break;
269        }
270    }
271
272    /**
273     * Apply a gravity constant to an object.
274     *
275     * @param gravity The desired placement of the object, as defined by the
276     *                constants in this class.
277     * @param w The horizontal size of the object.
278     * @param h The vertical size of the object.
279     * @param container The frame of the containing space, in which the object
280     *                  will be placed.  Should be large enough to contain the
281     *                  width and height of the object.
282     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
283     *             pushes it to the right; if gravity is RIGHT it pushes it to
284     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
285     *             right or left; otherwise it is ignored.
286     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
287     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
288     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
289     *             ignored.
290     * @param outRect Receives the computed frame of the object in its
291     *                container.
292     * @param layoutDirection The layout direction.
293     *
294     * @see {@link View#LAYOUT_DIRECTION_LTR}
295     * @see {@link View#LAYOUT_DIRECTION_RTL}
296     * @hide
297     */
298    public static void apply(int gravity, int w, int h, Rect container,
299                             int xAdj, int yAdj, Rect outRect, int layoutDirection) {
300        int absGravity = getAbsoluteGravity(gravity, layoutDirection);
301        apply(absGravity, w, h, container, xAdj, yAdj, outRect);
302    }
303
304    /**
305     * Apply additional gravity behavior based on the overall "display" that an
306     * object exists in.  This can be used after
307     * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
308     * within a visible display.  By default this moves or clips the object
309     * to be visible in the display; the gravity flags
310     * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
311     * can be used to change this behavior.
312     *
313     * @param gravity Gravity constants to modify the placement within the
314     * display.
315     * @param display The rectangle of the display in which the object is
316     * being placed.
317     * @param inoutObj Supplies the current object position; returns with it
318     * modified if needed to fit in the display.
319     */
320    public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
321        if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
322            if (inoutObj.top < display.top) inoutObj.top = display.top;
323            if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
324        } else {
325            int off = 0;
326            if (inoutObj.top < display.top) off = display.top-inoutObj.top;
327            else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
328            if (off != 0) {
329                if (inoutObj.height() > (display.bottom-display.top)) {
330                    inoutObj.top = display.top;
331                    inoutObj.bottom = display.bottom;
332                } else {
333                    inoutObj.top += off;
334                    inoutObj.bottom += off;
335                }
336            }
337        }
338
339        if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
340            if (inoutObj.left < display.left) inoutObj.left = display.left;
341            if (inoutObj.right > display.right) inoutObj.right = display.right;
342        } else {
343            int off = 0;
344            if (inoutObj.left < display.left) off = display.left-inoutObj.left;
345            else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
346            if (off != 0) {
347                if (inoutObj.width() > (display.right-display.left)) {
348                    inoutObj.left = display.left;
349                    inoutObj.right = display.right;
350                } else {
351                    inoutObj.left += off;
352                    inoutObj.right += off;
353                }
354            }
355        }
356    }
357
358    /**
359     * Apply additional gravity behavior based on the overall "display" that an
360     * object exists in.  This can be used after
361     * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
362     * within a visible display.  By default this moves or clips the object
363     * to be visible in the display; the gravity flags
364     * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
365     * can be used to change this behavior.
366     *
367     * @param gravity Gravity constants to modify the placement within the
368     * display.
369     * @param display The rectangle of the display in which the object is
370     * being placed.
371     * @param inoutObj Supplies the current object position; returns with it
372     * modified if needed to fit in the display.
373     * @param layoutDirection The layout direction.
374     *
375     * @see {@link View#LAYOUT_DIRECTION_LTR}
376     * @see {@link View#LAYOUT_DIRECTION_RTL}
377     * @hide
378     */
379    public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
380        int absGravity = getAbsoluteGravity(gravity, layoutDirection);
381        applyDisplay(absGravity, display, inoutObj);
382    }
383
384    /**
385     * <p>Indicate whether the supplied gravity has a vertical pull.</p>
386     *
387     * @param gravity the gravity to check for vertical pull
388     * @return true if the supplied gravity has a vertical pull
389     */
390    public static boolean isVertical(int gravity) {
391        return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
392    }
393
394    /**
395     * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
396     *
397     * @param gravity the gravity to check for horizontal pull
398     * @return true if the supplied gravity has an horizontal pull
399     */
400    public static boolean isHorizontal(int gravity) {
401        return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
402    }
403
404    /**
405     * <p>Convert script specific gravity to absolute horizontal value.</p>
406     *
407     * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
408     * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
409     *
410     *
411     * @param gravity The gravity to convert to absolute (horizontal) values.
412     * @param layoutDirection The layout direction.
413     * @return gravity converted to absolute (horizontal) values.
414     * @hide
415     */
416    public static int getAbsoluteGravity(int gravity, int layoutDirection) {
417        int result = gravity;
418        // If layout is script specific and gravity is horizontal relative (START or END)
419        if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
420            if ((result & Gravity.START) == Gravity.START) {
421                // Remove the START bit
422                result &= ~START;
423                if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
424                    // Set the RIGHT bit
425                    result |= RIGHT;
426                } else {
427                    // Set the LEFT bit
428                    result |= LEFT;
429                }
430            } else if ((result & Gravity.END) == Gravity.END) {
431                // Remove the END bit
432                result &= ~END;
433                if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
434                    // Set the LEFT bit
435                    result |= LEFT;
436                } else {
437                    // Set the RIGHT bit
438                    result |= RIGHT;
439                }
440            }
441            // Don't need the script specific bit any more, so remove it as we are converting to
442            // absolute values (LEFT or RIGHT)
443            result &= ~RELATIVE_LAYOUT_DIRECTION;
444        }
445        return result;
446    }
447}
448