1/* 2 * Copyright (C) 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "WKPDFPageNumberIndicator.h" 28 29#if PLATFORM(IOS) 30 31#import <QuartzCore/CAFilter.h> 32#import <QuartzCore/CALayerPrivate.h> 33#import <UIKit/UIGeometry_Private.h> 34#import <UIKit/UIKit.h> 35#import <UIKit/UIView_Private.h> 36#import <UIKit/_UIBackdropView_Private.h> 37#import <WebCore/LocalizedStrings.h> 38#import <wtf/RetainPtr.h> 39#import <wtf/text/WTFString.h> 40 41const CGFloat indicatorMargin = 20; 42const CGFloat indicatorVerticalPadding = 6; 43const CGFloat indicatorHorizontalPadding = 10; 44const CGFloat indicatorCornerRadius = 7; 45const CGFloat indicatorLabelOpacity = 0.4; 46const CGFloat indicatorFontSize = 16; 47 48const NSTimeInterval indicatorTimeout = 2; 49const NSTimeInterval indicatorFadeDuration = 0.5; 50const NSTimeInterval indicatorMoveDuration = 0.3; 51 52@implementation WKPDFPageNumberIndicator { 53 RetainPtr<UILabel> _label; 54 RetainPtr<_UIBackdropView> _backdropView; 55 RetainPtr<NSTimer> _timer; 56 57 bool _hasValidPageCountAndCurrentPage; 58} 59 60- (id)initWithFrame:(CGRect)frame 61{ 62 self = [super initWithFrame:frame]; 63 if (!self) 64 return nil; 65 66 self.alpha = 0; 67 self.layer.allowsGroupOpacity = NO; 68 self.layer.allowsGroupBlending = NO; 69 70 _backdropView = adoptNS([[_UIBackdropView alloc] initWithPrivateStyle:_UIBackdropViewStyle_Light]); 71 [_backdropView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 72 [self addSubview:_backdropView.get()]; 73 [self _makeRoundedCorners]; 74 75 _label = adoptNS([[UILabel alloc] initWithFrame:CGRectZero]); 76 77 [_label setOpaque:NO]; 78 [_label setBackgroundColor:nil]; 79 [_label setTextAlignment:NSTextAlignmentCenter]; 80 [_label setFont:[UIFont boldSystemFontOfSize:indicatorFontSize]]; 81 [_label setTextColor:[UIColor blackColor]]; 82 [_label setAlpha:indicatorLabelOpacity]; 83 [_label setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 84 [[_label layer] setCompositingFilter:[CAFilter filterWithType:kCAFilterPlusD]]; 85 86 [self addSubview:_label.get()]; 87 88 return self; 89} 90 91- (void)dealloc 92{ 93 [_timer invalidate]; 94 [super dealloc]; 95} 96 97- (void)setCurrentPageNumber:(unsigned)currentPageNumber 98{ 99 if (_currentPageNumber == currentPageNumber) 100 return; 101 102 _currentPageNumber = currentPageNumber; 103 [self _updateLabel]; 104} 105 106- (void)setPageCount:(unsigned)pageCount 107{ 108 if (_pageCount == pageCount) 109 return; 110 111 _pageCount = pageCount; 112 [self _updateLabel]; 113} 114 115- (void)show 116{ 117 self.alpha = 1; 118 119 if (_timer) 120 [_timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:indicatorTimeout]]; 121 else 122 _timer = [NSTimer scheduledTimerWithTimeInterval:indicatorTimeout target:self selector:@selector(hide:) userInfo:nil repeats:NO]; 123} 124 125- (void)hide:(NSTimer *)timer 126{ 127 [UIView animateWithDuration:indicatorFadeDuration animations:^{ 128 self.alpha = 0; 129 }]; 130 131 [_timer invalidate]; 132 _timer = nullptr; 133} 134 135- (void)moveToPoint:(CGPoint)point animated:(BOOL)animated 136{ 137 point.x += indicatorMargin; 138 point.y += indicatorMargin; 139 140 void (^animations)() = ^{ 141 self.frameOrigin = point; 142 }; 143 if (animated) 144 [UIView animateWithDuration:indicatorMoveDuration animations:animations]; 145 else 146 animations(); 147} 148 149- (CGSize)sizeThatFits:(CGSize)size 150{ 151 CGSize labelSize = [_label sizeThatFits:[_label size]]; 152 labelSize.width += 2 * indicatorHorizontalPadding; 153 labelSize.height += 2 * indicatorVerticalPadding; 154 return labelSize; 155} 156 157- (void)_updateLabel 158{ 159 [_label setText:[NSString localizedStringWithFormat:WEB_UI_STRING("%d of %d", "Label for PDF page number indicator."), _currentPageNumber, _pageCount]]; 160 [self sizeToFit]; 161 162 if (!_pageCount || !_currentPageNumber) 163 return; 164 165 // We don't want to show the indicator when the PDF loads, but rather on the first subsequent change. 166 if (_hasValidPageCountAndCurrentPage) 167 [self show]; 168 _hasValidPageCountAndCurrentPage = true; 169} 170 171- (void)_makeRoundedCorners 172{ 173 CGSize cornerImageSize = CGSizeMake(2 * indicatorCornerRadius + 2, 2 * indicatorCornerRadius + 2); 174 CGRect cornerImageBounds = CGRectMake(0, 0, cornerImageSize.width, cornerImageSize.height); 175 176 UIGraphicsBeginImageContextWithOptions(cornerImageSize, NO, [UIScreen mainScreen].scale); 177 178 CGContextRef context = UIGraphicsGetCurrentContext(); 179 CGContextSaveGState(context); 180 CGContextAddRect(context, cornerImageBounds); 181 182 UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:cornerImageBounds byRoundingCorners:(UIRectCorner)UIAllCorners cornerRadii:CGSizeMake(indicatorCornerRadius, indicatorCornerRadius)]; 183 CGContextAddPath(context, [cornerPath CGPath]); 184 CGContextEOClip(context); 185 CGContextFillRect(context, cornerImageBounds); 186 CGContextRestoreGState(context); 187 188 UIImage *cornerImage = UIGraphicsGetImageFromCurrentImageContext(); 189 190 UIGraphicsEndImageContext(); 191 192 UIView *contentView = [_backdropView contentView]; 193 194 UIEdgeInsets capInsets = UIEdgeInsetsMake(indicatorCornerRadius, indicatorCornerRadius, indicatorCornerRadius, indicatorCornerRadius); 195 cornerImage = [cornerImage resizableImageWithCapInsets:capInsets]; 196 197 RetainPtr<UIImageView> cornerMaskView = adoptNS([[UIImageView alloc] initWithImage:cornerImage]); 198 [cornerMaskView setAlpha:0]; 199 [cornerMaskView _setBackdropMaskViewFlags:_UIBackdropMaskViewAll]; 200 [cornerMaskView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 201 [cornerMaskView setFrame:contentView.bounds]; 202 203 [contentView addSubview:cornerMaskView.get()]; 204} 205 206@end 207 208#endif /* PLATFORM(IOS) */ 209