WKQuickboardListViewController.mm   [plain text]


/*
 * 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 "WKQuickboardListViewController.h"

#if PLATFORM(WATCHOS)

#import <wtf/RetainPtr.h>

static const CGFloat itemCellTopToLabelBaseline = 26;
static const CGFloat itemCellBaselineToBottom = 8;

@implementation WKQuickboardListItemCell

- (CGFloat)topToLabelBaselineSpecValue
{
    return itemCellTopToLabelBaseline;
}

- (CGFloat)baselineToBottomSpecValue
{
    return itemCellBaselineToBottom;
}

@end

#if HAVE(QUICKBOARD_COLLECTION_VIEWS)

@implementation WKQuickboardListCollectionViewItemCell

- (CGFloat)topToLabelBaselineSpecValue
{
    return itemCellTopToLabelBaseline;
}

- (CGFloat)baselineToBottomSpecValue
{
    return itemCellBaselineToBottom;
}

@end

#endif // HAVE(QUICKBOARD_COLLECTION_VIEWS)

@interface WKQuickboardListViewController () <PUICQuickboardLanguageControllerDelegate>
@end

@implementation WKQuickboardListViewController {
    BOOL _contextViewNeedsUpdate;
    RetainPtr<UIView> _contextView;
    RetainPtr<PUICStatusBarGlobalContextViewAssertion> _statusBarAssertion;
}

@dynamic delegate;

- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate
{
#if USE(APPLE_INTERNAL_SDK)
    if (self = [super initWithDelegate:delegate dictationMode:PUICDictationModeText])
        _contextViewNeedsUpdate = YES;
#else
    if (self = [super initWithDelegate:delegate])
        _contextViewNeedsUpdate = YES;
#endif

    return self;
}

- (void)updateContextViewIfNeeded
{
    if (!_contextViewNeedsUpdate)
        return;

    auto previousContextView = _contextView;
    if ([self.delegate shouldDisplayInputContextViewForListViewController:self])
        _contextView = [self.delegate inputContextViewForViewController:self];
    else
        _contextView = nil;

    _contextViewNeedsUpdate = NO;
}

- (BOOL)prefersStatusBarHidden
{
    return NO;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.headerView.hidden = YES;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    _statusBarAssertion = [[PUICApplication sharedPUICApplication] _takeStatusBarGlobalContextAssertionAnimated:NO];

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(_handleStatusBarNavigation) name:PUICStatusBarNavigationBackButtonPressedNotification object:nil];
    [center addObserver:self selector:@selector(_handleStatusBarNavigation) name:PUICStatusBarTitleTappedNotification object:nil];

    configureStatusBarForController(self, self.delegate);
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];

    _statusBarAssertion = nil;

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self name:PUICStatusBarNavigationBackButtonPressedNotification object:nil];
    [center removeObserver:self name:PUICStatusBarTitleTappedNotification object:nil];
}

- (void)_handleStatusBarNavigation
{
    [self.delegate quickboardInputCancelled:self];
}

- (void)reloadContextView
{
    _contextViewNeedsUpdate = YES;
    [self reloadHeaderContentView];
}

ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGIN

- (PUICActionController *)actionController
{
    if (![self.delegate allowsLanguageSelectionMenuForListViewController:self])
        return nil;

    PUICActionItem *languageSelectionActionItem = [self.languageController languageSelectionActionItemForViewController:self];
    auto actionGroup = adoptNS([[PUICActionGroup alloc] initWithActionItems:@[ languageSelectionActionItem ] actionStyle:PUICActionStyleAutomatic]);
    return [[[PUICActionController alloc] initWithActionGroup:actionGroup.get()] autorelease];
}

ALLOW_DEPRECATED_IMPLEMENTATIONS_END

#pragma mark - PUICQuickboardLanguageControllerDelegate

- (void)languageControllerDidChangePrimaryLanguage:(PUICQuickboardLanguageController *)languageController
{
    if ([self.delegate respondsToSelector:@selector(quickboard:languageDidChange:)])
        [self.delegate quickboard:self languageDidChange:languageController.primaryLanguage];
}

#pragma mark - Quickboard subclassing

- (CGFloat)headerContentViewHeight
{
    [self updateContextViewIfNeeded];

    return [_contextView sizeThatFits:self.contentView.bounds.size].height;
}

- (UIView *)headerContentView
{
    [self updateContextViewIfNeeded];

    CGFloat viewWidth = CGRectGetWidth(self.contentView.bounds);
    CGSize sizeThatFits = [_contextView sizeThatFits:self.contentView.bounds.size];
    [_contextView setFrame:CGRectMake((viewWidth - sizeThatFits.width) / 2, 0, sizeThatFits.width, sizeThatFits.height)];
    [_contextView layoutSubviews];
    return _contextView.get();
}

@end

void configureStatusBarForController(PUICQuickboardViewController *controller, id <WKQuickboardViewControllerDelegate> delegate)
{
    // An internal client (e.g. Safari's modal sheet) may have moved the status bar offscreen.
    // Before updating the status bar, make sure that we bring the status bar back to its original position.
    PUICStatusBar *statusBar = [PUICApplication sharedPUICApplication]._puicStatusBar;
    statusBar.frame = CGRect { CGPointZero, statusBar.frame.size };

    PUICApplicationStatusBarItem *item = controller.puic_applicationStatusBarItem;
    item.title = [delegate inputLabelTextForViewController:controller];
    item.titleColor = [UIColor systemBlueColor];
    item.navUIBackButtonDisabled = NO;
    item.showNavigationUI = YES;
    item.titleInteractive = YES;
    [item commitChangesAnimated:NO];
}

#endif // PLATFORM(WATCHOS)