Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Unified Diff: ios/chrome/browser/ui/uikit_ui_util.mm

Issue 802633007: Upstream iOS UI utilities. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/uikit_ui_util.mm
diff --git a/ios/chrome/browser/ui/uikit_ui_util.mm b/ios/chrome/browser/ui/uikit_ui_util.mm
new file mode 100644
index 0000000000000000000000000000000000000000..fa96ffde79bde7f15a43e62967ca3e48986567da
--- /dev/null
+++ b/ios/chrome/browser/ui/uikit_ui_util.mm
@@ -0,0 +1,460 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+
+#import <Accelerate/Accelerate.h>
+#import <Foundation/Foundation.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/ios/ios_util.h"
+#include "base/mac/foundation_util.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/gfx/scoped_cg_context_save_gstate_mac.h"
+#include "ui/ios/uikit_util.h"
+
+namespace {
+
+// Linearly interpolate between |a| and |b| by fraction |f|. Satisfies
+// |lerp(a,b,0) == a| and |lerp(a,b,1) == b|.
sdefresne 2015/01/13 15:19:25 nit: s/lerp/Lerp/g
+CGFloat Lerp(CGFloat a, CGFloat b, CGFloat fraction) {
+ return a * (1.0f - fraction) + b * fraction;
+}
+
+// Gets the RGBA components from a UIColor in RBG or monochrome color space.
+void GetRGBA(UIColor* color, CGFloat* r, CGFloat* g, CGFloat* b, CGFloat* a) {
+ switch (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor))) {
+ case kCGColorSpaceModelRGB: {
+ BOOL success = [color getRed:r green:g blue:b alpha:a];
+ DCHECK(success);
+ return;
+ }
+ case kCGColorSpaceModelMonochrome: {
+ const size_t componentsCount =
+ CGColorGetNumberOfComponents(color.CGColor);
+ DCHECK(componentsCount == 1 || componentsCount == 2);
+ const CGFloat* components = CGColorGetComponents(color.CGColor);
+ *r = components[0];
+ *g = components[0];
+ *b = components[0];
+ *a = componentsCount == 1 ? 1 : components[1];
+ return;
+ }
+ default:
+ NOTREACHED() << "Unsupported color space.";
+ return;
+ }
+}
+
+} // namespace
+
+void SetA11yLabelAndUiAutomationName(UIView* element,
+ int idsAccessibilityLabel,
+ NSString* englishUiAutomationName) {
+ [element setAccessibilityLabel:l10n_util::GetNSString(idsAccessibilityLabel)];
+ [element setAccessibilityIdentifier:englishUiAutomationName];
+}
+
+void GetSizeButtonWidthToFit(UIButton* button) {
+ // Resize the button's width to fit the new text, but keep the original
+ // height. sizeToFit appears to ignore the image size, so re-add the size of
+ // the button's image to the frame width.
+ CGFloat buttonHeight = [button frame].size.height;
+ CGFloat imageWidth = [[button imageView] frame].size.width;
+ [button sizeToFit];
+ CGRect newFrame = [button frame];
+ newFrame.size.height = buttonHeight;
+ newFrame.size.width += imageWidth;
+ [button setFrame:newFrame];
+}
+
+void TranslateFrame(UIView* view, UIOffset offset) {
+ if (!view)
+ return;
+
+ CGRect frame = [view frame];
+ frame.origin.x = frame.origin.x + offset.horizontal;
+ frame.origin.y = frame.origin.y + offset.vertical;
+ [view setFrame:frame];
+}
+
+UIFont* GetUIFont(int fontFace, bool isBold, CGFloat fontSize) {
+ NSString* fontFaceName;
+ switch (fontFace) {
+ case FONT_HELVETICA:
+ fontFaceName = isBold ? @"Helvetica-Bold" : @"Helvetica";
+ break;
+ case FONT_HELVETICA_NEUE:
+ fontFaceName = isBold ? @"HelveticaNeue-Bold" : @"HelveticaNeue";
+ break;
+ case FONT_HELVETICA_NEUE_LIGHT:
+ // FONT_HELVETICA_NEUE_LIGHT does not support Bold.
+ DCHECK(!isBold);
+ fontFaceName = @"HelveticaNeue-Light";
+ break;
+ default:
+ NOTREACHED();
+ fontFaceName = @"Helvetica";
+ break;
+ }
+ return [UIFont fontWithName:fontFaceName size:fontSize];
+}
+
+void AddBorderShadow(UIView* view, CGFloat offset, UIColor* color) {
+ CGRect rect = CGRectInset(view.bounds, -offset, -offset);
+ CGPoint waypoints[] = {
+ CGPointMake(rect.origin.x, rect.origin.y),
+ CGPointMake(rect.origin.x, rect.origin.y + rect.size.height),
+ CGPointMake(rect.origin.x + rect.size.width,
+ rect.origin.y + rect.size.height),
+ CGPointMake(rect.origin.x + rect.size.width, rect.origin.y),
+ CGPointMake(rect.origin.x, rect.origin.y)};
+ int numberOfWaypoints = sizeof(waypoints) / sizeof(waypoints[0]);
+ CGMutablePathRef outline = CGPathCreateMutable();
+ CGPathAddLines(outline, NULL, waypoints, numberOfWaypoints);
+ view.layer.shadowColor = [color CGColor];
+ view.layer.shadowOpacity = 1.0;
+ view.layer.shadowOffset = CGSizeZero;
+ view.layer.shadowPath = outline;
+ CGPathRelease(outline);
+}
+
+// TODO(pkl): The implementation of this has some duplicated code with
+// AddBorderShadow and ToolsPopupView newPathForRect:withRadius:withArrow:.
+// There is an opportunity to refactor them into a common shadow library.
+void AddRoundedBorderShadow(UIView* view, CGFloat radius, UIColor* color) {
+ CGRect rect = view.bounds;
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGFloat minX = CGRectGetMinX(rect);
+ CGFloat midX = CGRectGetMidX(rect);
+ CGFloat maxX = CGRectGetMaxX(rect);
+ CGFloat minY = CGRectGetMinY(rect);
+ CGFloat midY = CGRectGetMidY(rect);
+ CGFloat maxY = CGRectGetMaxY(rect);
+ CGPathMoveToPoint(path, NULL, minX, midY);
+ CGPathAddArcToPoint(path, NULL, minX, minY, midX, minY, radius);
+ CGPathAddArcToPoint(path, NULL, maxX, minY, maxX, midY, radius);
+ CGPathAddArcToPoint(path, NULL, maxX, maxY, midX, maxY, radius);
+ CGPathAddArcToPoint(path, NULL, minX, maxY, minX, midY, radius);
+ CGPathCloseSubpath(path);
+ view.layer.shadowColor = [color CGColor];
+ view.layer.shadowOpacity = 1.0;
+ view.layer.shadowRadius = radius;
+ view.layer.shadowOffset = CGSizeZero;
+ view.layer.shadowPath = path;
+ view.layer.borderWidth = 0.0;
+ CGPathRelease(path);
+}
+
+UIImage* CaptureView(UIView* view, CGFloat scale) {
+ UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES /* opaque */,
+ scale);
+ CGContext* context = UIGraphicsGetCurrentContext();
+ [view.layer renderInContext:context];
+ UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return image;
+}
+
+UIImage* GreyImage(UIImage* image) {
+ DCHECK(image);
+ // Grey images are always non-retina to improve memory performance.
+ UIGraphicsBeginImageContextWithOptions(image.size, YES, 1.0);
+ CGRect greyImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
+ [image drawInRect:greyImageRect blendMode:kCGBlendModeLuminosity alpha:1.0];
+ UIImage* greyImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return greyImage;
+}
+
+UIColor* GetPrimaryActionButtonColor() {
+ return UIColorFromRGB(0x2d6ada, 1.0);
+}
+
+UIColor* GetSettingsBackgroundColor() {
+ CGFloat rgb = 237.0 / 255.0;
+ return [UIColor colorWithWhite:rgb alpha:1];
+}
+
+UIImage* ResizeImage(UIImage* image,
+ CGSize targetSize,
+ BOOL preserveAspectRatio,
+ BOOL trimToFit) {
+ CGSize revisedTargetSize;
+ CGRect projectTo;
+
+ CalculateProjection([image size], targetSize, preserveAspectRatio, trimToFit,
+ revisedTargetSize, projectTo);
+
+ if (CGRectEqualToRect(projectTo, CGRectZero))
+ return nil;
+
+ // Resize photo. Use UIImage drawing methods because they respect
+ // UIImageOrientation as opposed to CGContextDrawImage().
+ UIGraphicsBeginImageContextWithOptions(revisedTargetSize, NO, image.scale);
+ [image drawInRect:projectTo];
+ UIImage* resizedPhoto = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return resizedPhoto;
+}
+
+UIImage* DarkenImage(UIImage* image) {
+ UIColor* tintColor = [UIColor colorWithWhite:0.22 alpha:0.6];
+ return BlurImage(image,
+ 3.0, // blurRadius,
+ tintColor,
+ 1.8, // saturationDeltaFactor
+ nil);
+}
+
+UIImage* BlurImage(UIImage* image,
+ CGFloat blurRadius,
+ UIColor* tintColor,
+ CGFloat saturationDeltaFactor,
+ UIImage* maskImage) {
+ // This code is heavily inspired by the UIImageEffect sample project,
+ // presented at WWDC and available from Apple.
+ DCHECK(image.size.width >= 1 && image.size.height >= 1);
+ DCHECK(image.CGImage);
+ DCHECK(!maskImage || maskImage.CGImage);
+
+ CGRect imageRect = {CGPointZero, image.size};
+ UIImage* effectImage = nil;
+
+ BOOL hasBlur = blurRadius > __FLT_EPSILON__;
+ BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
+ if (hasBlur || hasSaturationChange) {
+ UIGraphicsBeginImageContextWithOptions(image.size,
+ NO, // opaque.
+ [[UIScreen mainScreen] scale]);
+ CGContextRef effectInContext = UIGraphicsGetCurrentContext();
+ CGContextScaleCTM(effectInContext, 1.0, -1.0);
+ CGContextTranslateCTM(effectInContext, 0, -image.size.height);
+ CGContextDrawImage(effectInContext, imageRect, image.CGImage);
+
+ vImage_Buffer effectInBuffer;
+ effectInBuffer.data = CGBitmapContextGetData(effectInContext);
+ effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
+ effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
+ effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
+
+ UIGraphicsBeginImageContextWithOptions(image.size,
+ NO, // opaque.
+ [[UIScreen mainScreen] scale]);
+ CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
+ vImage_Buffer effectOutBuffer;
+ effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
+ effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
+ effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
+ effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
+
+ // Those are swapped as effects are applied.
+ vImage_Buffer* inBuffer = &effectInBuffer;
+ vImage_Buffer* outBuffer = &effectOutBuffer;
+
+ if (hasBlur) {
+ // A description of how to compute the box kernel width from the Gaussian
+ // radius (aka standard deviation) appears in the SVG spec:
+ // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
+ //
+ // For larger values of 's' (s >= 2.0), an approximation can be used:
+ // Three successive box-blurs build a piece-wise quadratic convolution
+ // kernel, which approximates the Gaussian kernel to within roughly 3%.
+ //
+ // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
+ //
+ // ... if d is odd, use three box-blurs of size 'd', centered on the
+ // output pixel.
+ //
+ CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
+ NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
+ if (radius % 2 != 1) {
+ // force radius to be odd so that the three box-blur methodology works.
+ radius += 1;
+ }
+ for (int i = 0; i < 3; ++i) {
+ vImageBoxConvolve_ARGB8888(inBuffer, // src.
+ outBuffer, // dst.
+ NULL, // tempBuffer.
+ 0, // srcOffsetToROI_X.
+ 0, // srcOffsetToROI_Y
+ radius, // kernel_height
+ radius, // kernel_width
+ 0, // backgroundColor
+ kvImageEdgeExtend); // flags
+ vImage_Buffer* temp = inBuffer;
+ inBuffer = outBuffer;
+ outBuffer = temp;
+ }
+ }
+ if (hasSaturationChange) {
+ CGFloat s = saturationDeltaFactor;
+ CGFloat floatingPointSaturationMatrix[] = {
+ 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
+ 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
+ 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
+ 0, 0, 0, 1 };
+ const int32_t divisor = 256;
+ NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix) /
+ sizeof(floatingPointSaturationMatrix[0]);
+ int16_t saturationMatrix[matrixSize];
+ for (NSUInteger i = 0; i < matrixSize; ++i) {
+ saturationMatrix[i] =
+ (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
+ }
+ vImageMatrixMultiply_ARGB8888(inBuffer, outBuffer, saturationMatrix,
+ divisor, NULL, NULL, kvImageNoFlags);
+ }
+ if (outBuffer == &effectOutBuffer)
+ effectImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ if (!effectImage)
+ effectImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ }
+
+ // Set up output context.
+ UIGraphicsBeginImageContextWithOptions(image.size,
+ NO, // opaque
+ [[UIScreen mainScreen] scale]);
+ CGContextRef outputContext = UIGraphicsGetCurrentContext();
+ CGContextScaleCTM(outputContext, 1.0, -1.0);
+ CGContextTranslateCTM(outputContext, 0, -image.size.height);
+
+ // Draw base image.
+ CGContextDrawImage(outputContext, imageRect, image.CGImage);
+
+ // Draw effect image.
+ if (effectImage) {
+ gfx::ScopedCGContextSaveGState context(outputContext);
+ if (maskImage)
+ CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
+ CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
+ }
+
+ // Add in color tint.
+ if (tintColor) {
+ gfx::ScopedCGContextSaveGState context(outputContext);
+ CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
+ CGContextFillRect(outputContext, imageRect);
+ }
+
+ // Output image is ready.
+ UIImage* outputImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ return outputImage;
+}
+
+UIInterfaceOrientation GetInterfaceOrientation() {
+ return [[UIApplication sharedApplication] statusBarOrientation];
+}
+
+CGFloat CurrentKeyboardHeight(NSValue* keyboardFrameValue) {
+ CGSize keyboardSize = [keyboardFrameValue CGRectValue].size;
+ if (base::ios::IsRunningOnIOS8OrLater()) {
+ return keyboardSize.height;
+ } else {
+ return IsPortrait() ? keyboardSize.height : keyboardSize.width;
+ }
+}
+
+UIImage* ImageWithColor(UIColor* color) {
+ CGRect rect = CGRectMake(0, 0, 1, 1);
+ UIGraphicsBeginImageContext(rect.size);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ CGContextSetFillColorWithColor(context, [color CGColor]);
+ CGContextFillRect(context, rect);
+ UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return image;
+}
+
+UIImage* CircularImageFromImage(UIImage* image, CGFloat width) {
+ CGRect frame =
+ CGRectMakeAlignedAndCenteredAt(width / 2.0, width / 2.0, width);
+
+ UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0.0);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ CGContextBeginPath(context);
+ CGContextAddEllipseInRect(context, frame);
+ CGContextClosePath(context);
+ CGContextClip(context);
+
+ CGFloat scaleX = frame.size.width / image.size.width;
+ CGFloat scaleY = frame.size.height / image.size.height;
+ CGFloat scale = std::max(scaleX, scaleY);
+ CGContextScaleCTM(context, scale, scale);
+
+ [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
+
+ image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ return image;
+}
+
+UIColor* InterpolateFromColorToColor(UIColor* firstColor,
+ UIColor* secondColor,
+ CGFloat fraction) {
+ DCHECK_LE(0.0, fraction);
+ DCHECK_LE(fraction, 1.0);
+ CGFloat r1, r2, g1, g2, b1, b2, a1, a2;
+ GetRGBA(firstColor, &r1, &g1, &b1, &a1);
+ GetRGBA(secondColor, &r2, &g2, &b2, &a2);
+ return [UIColor colorWithRed:Lerp(r1, r2, fraction)
+ green:Lerp(g1, g2, fraction)
+ blue:Lerp(b1, b2, fraction)
+ alpha:Lerp(a1, a2, fraction)];
+}
+
+void ApplyVisualConstraints(NSArray* constraints,
+ NSDictionary* subviewsDictionary,
+ UIView* parentView) {
+ for (NSString* constraint in constraints) {
+ DCHECK([constraint isKindOfClass:[NSString class]]);
+ [parentView
+ addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:constraint
+ options:0
+ metrics:nil
+ views:subviewsDictionary]];
+ }
+}
+
+void AddSameCenterXConstraint(UIView* parentView, UIView* subview) {
+ DCHECK_EQ(parentView, [subview superview]);
+ [parentView addConstraint:[NSLayoutConstraint
+ constraintWithItem:subview
+ attribute:NSLayoutAttributeCenterX
+ relatedBy:NSLayoutRelationEqual
+ toItem:parentView
+ attribute:NSLayoutAttributeCenterX
+ multiplier:1
+ constant:0]];
+}
+
+void AddSameCenterYConstraint(UIView* parentView,
+ UIView* subview1,
+ UIView* subview2) {
+ DCHECK_EQ(parentView, [subview1 superview]);
+ DCHECK_EQ(parentView, [subview2 superview]);
+ DCHECK_NE(subview1, subview2);
+ [parentView addConstraint:[NSLayoutConstraint
+ constraintWithItem:subview1
+ attribute:NSLayoutAttributeCenterY
+ relatedBy:NSLayoutRelationEqual
+ toItem:subview2
+ attribute:NSLayoutAttributeCenterY
+ multiplier:1
+ constant:0]];
+}

Powered by Google App Engine
This is Rietveld 408576698