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>
10 #include "base/ios/ios_util.h"
11 #include "base/logging.h"
12 #include "ios/chrome/browser/ui/ui_util.h"
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,
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) {
64 if (direction == base::i18n::RIGHT_TO_LEFT) {
66 layout.boundingWidth - (layout.position.leading + layout.size.width);
67 rect = CGRectMake(trailing, layout.position.originY, layout.size.width,
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);
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(
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,
99 LayoutRect LayoutRectForRectInBoundingRectUsingDirection(
102 base::i18n::TextDirection direction) {
104 if (direction == base::i18n::RIGHT_TO_LEFT) {
105 layout.position.leading = contextRect.size.width - CGRectGetMaxX(rect);
107 layout.position.leading = CGRectGetMinX(rect);
109 layout.boundingWidth = contextRect.size.width;
110 layout.position.originY = rect.origin.y;
111 layout.size = rect.size;
115 LayoutRect LayoutRectForRectInBoundingRect(CGRect rect, CGRect contextRect) {
116 return LayoutRectForRectInBoundingRectUsingDirection(rect, contextRect,
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;
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,
146 base::i18n::TextDirection direction) {
147 CGPoint newPoint = point;
148 if (direction == base::i18n::RIGHT_TO_LEFT)
150 newPoint.x += offset;
154 CGPoint CGPointLayoutOffset(CGPoint point, LayoutOffset offset) {
155 return CGPointLayoutOffsetUsingDirection(point, offset, LayoutDirection());
158 CGRect CGRectLayoutOffsetUsingDirection(CGRect rect,
160 base::i18n::TextDirection direction) {
161 if (direction == base::i18n::RIGHT_TO_LEFT)
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
178 offset = rectLeadingEdge - boundingRectLeadingEdge;
180 DCHECK_EQ(LayoutDirection(), base::i18n::RIGHT_TO_LEFT);
181 offset = boundingRectLeadingEdge - rectLeadingEdge;
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
194 offset = rectTrailingEdge - boundingRectTrailingEdge;
196 DCHECK_EQ(LayoutDirection(), base::i18n::LEFT_TO_RIGHT);
197 offset = boundingRectTrailingEdge - rectTrailingEdge;
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(
213 base::i18n::TextDirection direction) {
214 return direction == base::i18n::RIGHT_TO_LEFT ? CGRectGetMaxX(rect)
215 : CGRectGetMinX(rect);
218 CGFloat CGRectGetTrailingEdgeUsingDirection(
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(
250 base::i18n::TextDirection direction) {
251 if (direction == base::i18n::RIGHT_TO_LEFT) {
253 swap(leading, trailing);
255 // At this point, |leading| == left, |trailing| = right.
256 return UIEdgeInsetsMake(top, leading, bottom, trailing);
259 UIEdgeInsets UIEdgeInsetsMakeDirected(CGFloat top,
263 return UIEdgeInsetsMakeUsingDirection(top, leading, bottom, trailing,
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;
281 return NSLayoutFormatDirectionLeftToRight;