/* * Copyright (C) 2018 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "WKSelectMenuListViewController.h" #if HAVE(PEPPER_UI_CORE) #import "UIKitSPI.h" #import static const CGFloat checkmarkImageViewWidth = 32; static const CGFloat selectMenuItemHorizontalMargin = 9; static const CGFloat selectMenuItemCellHeight = 44; static NSString * const selectMenuCellReuseIdentifier = @"WebKitSelectMenuItemCell"; typedef NS_ENUM(NSInteger, PUICQuickboardListSection) { PUICQuickboardListSectionHeaderView, PUICQuickboardListSectionTrayButtons, PUICQuickboardListSectionTextOptions, PUICQuickboardListSectionContentUnavailable, }; static constexpr CGFloat itemCellTopToLabelBaseline = 26; static constexpr CGFloat itemCellBaselineToBottom = 8; // FIXME: This can be removed when lands in a build. @interface WKSelectMenuItemCell : PUICQuickboardListItemCell @property (nonatomic, readonly) UIImageView *imageView; @end @implementation WKSelectMenuItemCell { RetainPtr _imageView; } - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (!(self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) return nil; _imageView = adoptNS([[UIImageView alloc] init]); UIImage *checkmarkImage = [PUICResources imageNamed:@"UIPreferencesBlueCheck" inBundle:[NSBundle bundleWithIdentifier:@"com.apple.PepperUICore"] shouldCache:YES]; [_imageView setImage:[checkmarkImage _flatImageWithColor:[UIColor systemBlueColor]]]; [_imageView setContentMode:UIViewContentModeCenter]; [_imageView setHidden:YES]; [self.contentView addSubview:_imageView.get()]; return self; } - (UIImageView *)imageView { return _imageView.get(); } - (CGFloat)topToLabelBaselineSpecValue { return itemCellTopToLabelBaseline; } - (CGFloat)baselineToBottomSpecValue { return itemCellBaselineToBottom; } @end #if HAVE(QUICKBOARD_COLLECTION_VIEWS) @interface WKSelectMenuCollectionViewItemCell : PUICQuickboardListCollectionViewItemCell @property (nonatomic, readonly) UIImageView *imageView; @end @implementation WKSelectMenuCollectionViewItemCell { RetainPtr _imageView; } - (instancetype)initWithFrame:(CGRect)frame { if (!(self = [super initWithFrame:frame])) return nil; _imageView = adoptNS([[UIImageView alloc] init]); UIImage *checkmarkImage = [PUICResources imageNamed:@"UIPreferencesBlueCheck" inBundle:[NSBundle bundleWithIdentifier:@"com.apple.PepperUICore"] shouldCache:YES]; [_imageView setImage:[checkmarkImage _flatImageWithColor:[UIColor systemBlueColor]]]; [_imageView setContentMode:UIViewContentModeCenter]; [_imageView setHidden:YES]; [self.contentView addSubview:_imageView.get()]; return self; } - (UIImageView *)imageView { return _imageView.get(); } - (CGFloat)topToLabelBaselineSpecValue { return itemCellTopToLabelBaseline; } - (CGFloat)baselineToBottomSpecValue { return itemCellBaselineToBottom; } @end #endif // HAVE(QUICKBOARD_COLLECTION_VIEWS) @implementation WKSelectMenuListViewController { BOOL _isMultipleSelect; RetainPtr _indicesOfCheckedOptions; } @dynamic delegate; - (instancetype)initWithDelegate:(id )delegate { return [super initWithDelegate:delegate dictationMode:PUICDictationModeText]; } - (void)viewDidLoad { [super viewDidLoad]; self.cancelButton.hidden = YES; self.showsAcceptButton = YES; _isMultipleSelect = [self.delegate selectMenuUsesMultipleSelection:self]; _indicesOfCheckedOptions = adoptNS([[NSMutableIndexSet alloc] init]); for (NSInteger index = 0; index < self.numberOfListItems; ++index) { if ([self.delegate selectMenu:self hasSelectedOptionAtIndex:index]) [_indicesOfCheckedOptions addIndex:index]; } } #pragma mark - Quickboard subclassing - (void)acceptButtonTappedWithCompletion:(PUICQuickboardCompletionBlock)completion { completion(nil); } - (BOOL)shouldShowTrayView { return NO; } // FIXME: This method can be removed when lands in a build. - (void)didSelectListItem:(NSInteger)itemNumber { [self didSelectListItemAtIndexPath:[NSIndexPath indexPathForRow:itemNumber inSection:PUICQuickboardListSectionTextOptions]]; } - (void)didSelectListItemAtIndexPath:(NSIndexPath *)indexPath { NSMutableArray *indexPathsToReload = [NSMutableArray array]; NSInteger itemNumber = indexPath.row; if (_isMultipleSelect) { BOOL addIndex = ![_indicesOfCheckedOptions containsIndex:itemNumber]; if (addIndex) [_indicesOfCheckedOptions addIndex:itemNumber]; else [_indicesOfCheckedOptions removeIndex:itemNumber]; [self.delegate selectMenu:self didCheckItemAtIndex:itemNumber checked:addIndex]; [indexPathsToReload addObject:[NSIndexPath indexPathForRow:itemNumber inSection:indexPath.section]]; } else { NSInteger previousSelectedIndex = [_indicesOfCheckedOptions firstIndex]; if (previousSelectedIndex != itemNumber) { [_indicesOfCheckedOptions removeAllIndexes]; [_indicesOfCheckedOptions addIndex:itemNumber]; [self.delegate selectMenu:self didSelectItemAtIndex:itemNumber]; if (previousSelectedIndex != NSNotFound) [indexPathsToReload addObject:[NSIndexPath indexPathForRow:previousSelectedIndex inSection:indexPath.section]]; [indexPathsToReload addObject:[NSIndexPath indexPathForRow:itemNumber inSection:indexPath.section]]; } } if (!indexPathsToReload.count) return; if (self.listView) { [self.listView reloadRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationNone]; return; } #if HAVE(QUICKBOARD_COLLECTION_VIEWS) [self.collectionView reloadItemsAtIndexPaths:indexPathsToReload]; #endif } - (NSInteger)numberOfListItems { return [self.delegate numberOfItemsInSelectMenu:self]; } - (CGFloat)heightForListItem:(NSInteger)itemNumber width:(CGFloat)width { return selectMenuItemCellHeight; } // FIXME: This method can be removed when lands in a build. - (PUICQuickboardListItemCell *)cellForListItem:(NSInteger)itemNumber { auto reusableCell = retainPtr([self.listView dequeueReusableCellWithIdentifier:selectMenuCellReuseIdentifier]); if (!reusableCell) { reusableCell = adoptNS([[WKSelectMenuItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:selectMenuCellReuseIdentifier]); [reusableCell itemLabel].numberOfLines = 1; [reusableCell itemLabel].lineBreakMode = NSLineBreakByTruncatingTail; [reusableCell itemLabel].allowsDefaultTighteningForTruncation = YES; [reusableCell imageView].frame = UIRectInset([reusableCell contentView].bounds, 0, 0, 0, CGRectGetWidth([reusableCell contentView].bounds) - checkmarkImageViewWidth); } NSString *optionText = [self.delegate selectMenu:self displayTextForItemAtIndex:itemNumber]; [reusableCell configureForText:optionText width:CGRectGetWidth(self.listView.bounds)]; [reusableCell setRadioSectionCell:!_isMultipleSelect]; if ([_indicesOfCheckedOptions containsIndex:itemNumber]) { [reusableCell itemLabel].frame = UIRectInset([reusableCell contentView].bounds, 0, selectMenuItemHorizontalMargin + checkmarkImageViewWidth, 0, selectMenuItemHorizontalMargin); [reusableCell imageView].hidden = NO; } else { [reusableCell itemLabel].frame = UIRectInset([reusableCell contentView].bounds, 0, selectMenuItemHorizontalMargin, 0, selectMenuItemHorizontalMargin); [reusableCell imageView].hidden = YES; } return reusableCell.autorelease(); } - (NSString *)listItemCellReuseIdentifier { return selectMenuCellReuseIdentifier; } - (BOOL)shouldShowLanguageButton { return NO; } #if HAVE(QUICKBOARD_COLLECTION_VIEWS) - (Class)listItemCellClass { return [WKSelectMenuCollectionViewItemCell class]; } - (PUICQuickboardListCollectionViewItemCell *)itemCellForListItem:(NSInteger)itemNumber forIndexPath:(NSIndexPath *)indexPath { auto reusableCell = retainPtr([self.collectionView dequeueReusableCellWithReuseIdentifier:selectMenuCellReuseIdentifier forIndexPath:indexPath]); [reusableCell bodyLabel].numberOfLines = 1; [reusableCell bodyLabel].lineBreakMode = NSLineBreakByTruncatingTail; [reusableCell bodyLabel].allowsDefaultTighteningForTruncation = YES; [reusableCell imageView].frame = UIRectInset([reusableCell contentView].bounds, 0, 0, 0, CGRectGetWidth([reusableCell contentView].bounds) - checkmarkImageViewWidth); [reusableCell setText:[self.delegate selectMenu:self displayTextForItemAtIndex:itemNumber]]; if ([_indicesOfCheckedOptions containsIndex:itemNumber]) { [reusableCell bodyLabel].frame = UIRectInset([reusableCell contentView].bounds, 0, selectMenuItemHorizontalMargin + checkmarkImageViewWidth, 0, selectMenuItemHorizontalMargin); [reusableCell imageView].hidden = NO; } else { [reusableCell bodyLabel].frame = UIRectInset([reusableCell contentView].bounds, 0, selectMenuItemHorizontalMargin, 0, selectMenuItemHorizontalMargin); [reusableCell imageView].hidden = YES; } return reusableCell.autorelease(); } - (BOOL)collectionViewSectionIsRadioSection:(NSInteger)sectionNumber { return !_isMultipleSelect; } #endif // HAVE(QUICKBOARD_COLLECTION_VIEWS) - (void)selectItemAtIndex:(NSInteger)index { #if HAVE(QUICKBOARD_COLLECTION_VIEWS) NSInteger itemSection = 0; #else NSInteger itemSection = PUICQuickboardListSectionTextOptions; #endif [self didSelectListItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:itemSection]]; } @end #endif // HAVE(PEPPER_UI_CORE)