Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ios / chrome / browser / ui / rtl_geometry.mm
blob25df7564f83b839123f4f8d22aa24c60b41897e2
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/chrome/browser/ui/rtl_geometry.h"
7 #import <UIKit/UIKit.h>
8 #include <limits>
10 #include "base/ios/ios_util.h"
11 #include "base/logging.h"
12 #include "ios/chrome/browser/ui/ui_util.h"
14 bool UseRTLLayout() {
15   return base::i18n::IsRTL() && base::ios::IsRunningOnIOS9OrLater();
18 base::i18n::TextDirection LayoutDirection() {
19   return UseRTLLayout() ? base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
22 #pragma mark - LayoutRects.
24 const LayoutRectPosition LayoutRectPositionZero = {0.0, 0.0};
26 LayoutRectPosition LayoutRectPositionMake(CGFloat leading, CGFloat originY) {
27   return {leading, originY};
30 BOOL LayoutRectPositionEqualToPosition(LayoutRectPosition a,
31                                        LayoutRectPosition b) {
32   CGFloat epsilon = std::numeric_limits<CGFloat>::epsilon();
33   return fabs(a.leading - b.leading) <= epsilon &&
34          fabs(a.originY - b.originY) <= epsilon;
37 LayoutRectPosition AlignLayoutRectPositionToPixel(LayoutRectPosition position) {
38   return LayoutRectPositionMake(AlignValueToPixel(position.leading),
39                                 AlignValueToPixel(position.originY));
42 const LayoutRect LayoutRectZero = {0.0, LayoutRectPositionZero,
43                                    CGSizeMake(0.0, 0.0)};
45 LayoutRect LayoutRectMake(CGFloat leading,
46                           CGFloat boundingWidth,
47                           CGFloat originY,
48                           CGFloat width,
49                           CGFloat height) {
50   return {boundingWidth, LayoutRectPositionMake(leading, originY),
51           CGSizeMake(width, height)};
54 BOOL LayoutRectEqualToRect(LayoutRect a, LayoutRect b) {
55   CGFloat epsilon = std::numeric_limits<CGFloat>::epsilon();
56   return fabs(a.boundingWidth - b.boundingWidth) <= epsilon &&
57          LayoutRectPositionEqualToPosition(a.position, b.position) &&
58          CGSizeEqualToSize(a.size, b.size);
61 CGRect LayoutRectGetRectUsingDirection(LayoutRect layout,
62                                        base::i18n::TextDirection direction) {
63   CGRect rect;
64   if (direction == base::i18n::RIGHT_TO_LEFT) {
65     CGFloat trailing =
66         layout.boundingWidth - (layout.position.leading + layout.size.width);
67     rect = CGRectMake(trailing, layout.position.originY, layout.size.width,
68                       layout.size.height);
69   } else {
70     DCHECK_EQ(direction, base::i18n::LEFT_TO_RIGHT);
71     rect = CGRectMake(layout.position.leading, layout.position.originY,
72                       layout.size.width, layout.size.height);
73   }
74   return rect;
77 CGRect LayoutRectGetRect(LayoutRect layout) {
78   return LayoutRectGetRectUsingDirection(layout, LayoutDirection());
81 CGRect LayoutRectGetBoundsRect(LayoutRect layout) {
82   return CGRectMake(0, 0, layout.size.width, layout.size.height);
85 CGPoint LayoutRectGetPositionForAnchorUsingDirection(
86     LayoutRect layout,
87     CGPoint anchor,
88     base::i18n::TextDirection direction) {
89   CGRect rect = LayoutRectGetRectUsingDirection(layout, direction);
90   return CGPointMake(CGRectGetMinX(rect) + (rect.size.width * anchor.x),
91                      CGRectGetMinY(rect) + (rect.size.height * anchor.y));
94 CGPoint LayoutRectGetPositionForAnchor(LayoutRect layout, CGPoint anchor) {
95   return LayoutRectGetPositionForAnchorUsingDirection(layout, anchor,
96                                                       LayoutDirection());
99 LayoutRect LayoutRectForRectInBoundingRectUsingDirection(
100     CGRect rect,
101     CGRect contextRect,
102     base::i18n::TextDirection direction) {
103   LayoutRect layout;
104   if (direction == base::i18n::RIGHT_TO_LEFT) {
105     layout.position.leading = contextRect.size.width - CGRectGetMaxX(rect);
106   } else {
107     layout.position.leading = CGRectGetMinX(rect);
108   }
109   layout.boundingWidth = contextRect.size.width;
110   layout.position.originY = rect.origin.y;
111   layout.size = rect.size;
112   return layout;
115 LayoutRect LayoutRectForRectInBoundingRect(CGRect rect, CGRect contextRect) {
116   return LayoutRectForRectInBoundingRectUsingDirection(rect, contextRect,
117                                                        LayoutDirection());
120 LayoutRect LayoutRectGetLeadingLayout(LayoutRect layout) {
121   LayoutRect leadingLayout;
122   leadingLayout.position.leading = 0;
123   leadingLayout.boundingWidth = layout.boundingWidth;
124   leadingLayout.position.originY = leadingLayout.position.originY;
125   leadingLayout.size = CGSizeMake(layout.position.leading, layout.size.height);
126   return leadingLayout;
129 LayoutRect LayoutRectGetTrailingLayout(LayoutRect layout) {
130   LayoutRect leadingLayout;
131   CGFloat trailing = LayoutRectGetTrailingEdge(layout);
132   leadingLayout.position.leading = trailing;
133   leadingLayout.boundingWidth = layout.boundingWidth;
134   leadingLayout.position.originY = leadingLayout.position.originY;
135   leadingLayout.size =
136       CGSizeMake((layout.boundingWidth - trailing), layout.size.height);
137   return leadingLayout;
140 CGFloat LayoutRectGetTrailingEdge(LayoutRect layout) {
141   return layout.position.leading + layout.size.width;
144 CGPoint CGPointLayoutOffsetUsingDirection(CGPoint point,
145                                           LayoutOffset offset,
146                                           base::i18n::TextDirection direction) {
147   CGPoint newPoint = point;
148   if (direction == base::i18n::RIGHT_TO_LEFT)
149     offset = -offset;
150   newPoint.x += offset;
151   return newPoint;
154 CGPoint CGPointLayoutOffset(CGPoint point, LayoutOffset offset) {
155   return CGPointLayoutOffsetUsingDirection(point, offset, LayoutDirection());
158 CGRect CGRectLayoutOffsetUsingDirection(CGRect rect,
159                                         LayoutOffset offset,
160                                         base::i18n::TextDirection direction) {
161   if (direction == base::i18n::RIGHT_TO_LEFT)
162     offset = -offset;
163   return CGRectOffset(rect, offset, 0);
166 CGRect CGRectLayoutOffset(CGRect rect, LayoutOffset offset) {
167   return CGRectLayoutOffsetUsingDirection(rect, offset, LayoutDirection());
170 LayoutOffset CGRectGetLeadingLayoutOffsetInBoundingRect(CGRect rect,
171                                                         CGRect boundingRect) {
172   CGFloat rectLeadingEdge = CGRectGetLeadingEdge(rect);
173   CGFloat boundingRectLeadingEdge = CGRectGetLeadingEdge(boundingRect);
174   LayoutOffset offset = 0;
175   if (LayoutDirection() == base::i18n::LEFT_TO_RIGHT) {
176     // Leading edges have low x-values for LTR, so subtract the bounding rect's
177     // from |rect|'s.
178     offset = rectLeadingEdge - boundingRectLeadingEdge;
179   } else {
180     DCHECK_EQ(LayoutDirection(), base::i18n::RIGHT_TO_LEFT);
181     offset = boundingRectLeadingEdge - rectLeadingEdge;
182   }
183   return offset;
186 LayoutOffset CGRectGetTrailingLayoutOffsetInBoundingRect(CGRect rect,
187                                                          CGRect boundingRect) {
188   CGFloat rectTrailingEdge = CGRectGetTrailingEdge(rect);
189   CGFloat boundingRectTrailingEdge = CGRectGetTrailingEdge(boundingRect);
190   LayoutOffset offset = 0;
191   if (LayoutDirection() == base::i18n::RIGHT_TO_LEFT) {
192     // Trailing edges have low x-values for RTL, so subtract the bounding rect's
193     // from |rect|'s.
194     offset = rectTrailingEdge - boundingRectTrailingEdge;
195   } else {
196     DCHECK_EQ(LayoutDirection(), base::i18n::LEFT_TO_RIGHT);
197     offset = boundingRectTrailingEdge - rectTrailingEdge;
198   }
199   return offset;
202 LayoutOffset LeadingContentOffsetForScrollView(UIScrollView* scrollView) {
203   return UseRTLLayout()
204              ? scrollView.contentSize.width - scrollView.contentOffset.x -
205                    CGRectGetWidth(scrollView.bounds)
206              : scrollView.contentOffset.x;
209 #pragma mark - UIKit utilities
211 CGFloat CGRectGetLeadingEdgeUsingDirection(
212     CGRect rect,
213     base::i18n::TextDirection direction) {
214   return direction == base::i18n::RIGHT_TO_LEFT ? CGRectGetMaxX(rect)
215                                                 : CGRectGetMinX(rect);
218 CGFloat CGRectGetTrailingEdgeUsingDirection(
219     CGRect rect,
220     base::i18n::TextDirection direction) {
221   return direction == base::i18n::RIGHT_TO_LEFT ? CGRectGetMinX(rect)
222                                                 : CGRectGetMaxX(rect);
225 CGFloat CGRectGetLeadingEdge(CGRect rect) {
226   return CGRectGetLeadingEdgeUsingDirection(rect, LayoutDirection());
229 CGFloat CGRectGetTrailingEdge(CGRect rect) {
230   return CGRectGetTrailingEdgeUsingDirection(rect, LayoutDirection());
233 UIViewAutoresizing UIViewAutoresizingFlexibleLeadingMargin() {
234   return base::i18n::IsRTL() && base::ios::IsRunningOnIOS9OrLater()
235              ? UIViewAutoresizingFlexibleRightMargin
236              : UIViewAutoresizingFlexibleLeftMargin;
239 UIViewAutoresizing UIViewAutoresizingFlexibleTrailingMargin() {
240   return base::i18n::IsRTL() && base::ios::IsRunningOnIOS9OrLater()
241              ? UIViewAutoresizingFlexibleLeftMargin
242              : UIViewAutoresizingFlexibleRightMargin;
245 UIEdgeInsets UIEdgeInsetsMakeUsingDirection(
246     CGFloat top,
247     CGFloat leading,
248     CGFloat bottom,
249     CGFloat trailing,
250     base::i18n::TextDirection direction) {
251   if (direction == base::i18n::RIGHT_TO_LEFT) {
252     using std::swap;
253     swap(leading, trailing);
254   }
255   // At this point, |leading| == left, |trailing| = right.
256   return UIEdgeInsetsMake(top, leading, bottom, trailing);
259 UIEdgeInsets UIEdgeInsetsMakeDirected(CGFloat top,
260                                       CGFloat leading,
261                                       CGFloat bottom,
262                                       CGFloat trailing) {
263   return UIEdgeInsetsMakeUsingDirection(top, leading, bottom, trailing,
264                                         LayoutDirection());
267 CGFloat UIEdgeInsetsGetLeading(UIEdgeInsets insets) {
268   return UseRTLLayout() ? insets.right : insets.left;
271 CGFloat UIEdgeInsetsGetTrailing(UIEdgeInsets insets) {
272   return UseRTLLayout() ? insets.left : insets.right;
275 #pragma mark - autolayout utilities
277 NSLayoutFormatOptions LayoutOptionForRTLSupport() {
278   if (UseRTLLayout()) {
279     return NSLayoutFormatDirectionLeadingToTrailing;
280   }
281   return NSLayoutFormatDirectionLeftToRight;