[WebKit on watchOS] Upstream watchOS source additions to OpenSource (Part 2)
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Jun 2018 20:07:23 +0000 (20:07 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Jun 2018 20:07:23 +0000 (20:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186442
<rdar://problem/40879364>

Reviewed by Tim Horton.

Source/WebKit:

Upstream most of the work around form controls on watchOS. Also, rename WKFormControlListViewController.* to
its intended name, WKQuickboardListViewController.*.

* UIProcess/ios/WKContentViewInteraction.mm:
* UIProcess/ios/forms/WKDatePickerViewController.h:
* UIProcess/ios/forms/WKDatePickerViewController.mm:
(datePickerSetButtonHeight):
(datePickerVerticalMargin):
(-[WKDatePickerWheelLabel initWithFrame:]):
(-[WKDatePickerWheelLabel lastSelectedDate]):
(-[WKDatePickerWheelLabel setLastSelectedDate:]):
(-[WKDatePickerWheelLabel needsUpdateForIndex:selectedDate:]):
(-[WKDatePickerWheel initWithStyle:]):
(-[WKDatePickerWheel initWithController:style:]):
(-[WKDatePickerWheel gestureRecognized:]):
(-[WKDatePickerWheel setDrawsFocusOutline:]):
(-[WKDatePickerWheel drawsFocusOutline]):
(-[WKDatePickerWheel gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
(-[WKDatePickerViewController initWithDelegate:]):
(-[WKDatePickerViewController viewDidLoad]):
(-[WKDatePickerViewController prefersStatusBarHidden]):
(-[WKDatePickerViewController viewWillAppear:]):
(-[WKDatePickerViewController viewDidAppear:]):
(-[WKDatePickerViewController viewDidDisappear:]):
(-[WKDatePickerViewController _handleStatusBarNavigation]):
(-[WKDatePickerViewController viewWillLayoutSubviews]):
(-[WKDatePickerViewController becomeFirstResponder]):
(-[WKDatePickerViewController defaultMinimumDate]):
(-[WKDatePickerViewController defaultMaximumDate]):
(-[WKDatePickerViewController _valueForInput]):
(-[WKDatePickerViewController _dateFromInitialText]):
(-[WKDatePickerViewController _setButtonPressed]):
(-[WKDatePickerViewController _updateSelectedPickerViewIndices]):
(-[WKDatePickerViewController _configurePickerView:]):
(-[WKDatePickerViewController setMinimumDate:]):
(-[WKDatePickerViewController minimumDate]):
(-[WKDatePickerViewController setMaximumDate:]):
(-[WKDatePickerViewController maximumDate]):
(-[WKDatePickerViewController setDate:]):
(-[WKDatePickerViewController setDateFromComponents:]):
(-[WKDatePickerViewController setDay:month:year:era:]):
(-[WKDatePickerViewController date]):
(-[WKDatePickerViewController _dateComponentForDay:month:year:era:]):
(-[WKDatePickerViewController _adjustDateToValidDateIfNecessary]):
(-[WKDatePickerViewController _createAndConfigureGranularityLabelWithText:]):
(-[WKDatePickerViewController _canonicalizeAndUpdateSelectedDate]):
(-[WKDatePickerViewController numberOfItemsInPickerView:]):
(-[WKDatePickerViewController pickerView:viewForItemAtIndex:]):
(-[WKDatePickerViewController didBeginInteractingWithDatePicker:]):
(-[WKDatePickerViewController pickerView:didSelectItemAtIndex:]):
(-[WKDatePickerViewController pickerViewWillBeginSelection:]):
(-[WKDatePickerViewController pickerViewDidEndSelection:]):
(-[WKDatePickerViewController _dayFromIndex:]):
(-[WKDatePickerViewController _eraAndYearFromIndex:]):
(-[WKDatePickerViewController _monthFromIndex:]):
(-[WKDatePickerViewController _indexFromDay:]):
(-[WKDatePickerViewController _indexFromYear:era:]):
(-[WKDatePickerViewController _indexFromMonth:]):
* UIProcess/ios/forms/WKFormControlListViewController.h: Removed.
* UIProcess/ios/forms/WKFormControlListViewController.mm: Removed.
* UIProcess/ios/forms/WKNumberPadViewController.h:
* UIProcess/ios/forms/WKNumberPadViewController.mm:
(inputLabelFontSize):
(-[WKNumberPadViewController initWithDelegate:initialText:inputMode:]):
(-[WKNumberPadViewController dealloc]):
(-[WKNumberPadViewController viewDidLoad]):
(-[WKNumberPadViewController viewWillDisappear:]):
(-[WKNumberPadViewController viewWillLayoutSubviews]):
(-[WKNumberPadViewController _reloadHeaderViewFromInputText]):
(-[WKNumberPadViewController didSelectKey:]):
(-[WKNumberPadViewController _handleKeyPress:]):
(-[WKNumberPadViewController _cancelInput]):
(-[WKNumberPadViewController _deleteLastInputCharacter]):
(-[WKNumberPadViewController _deleteButtonPressed]):
(-[WKNumberPadViewController _cancelDeletionTimers]):
(-[WKNumberPadViewController _startDeletionTimer]):
(-[WKNumberPadViewController _deletionTimerFired]):
(-[WKNumberPadViewController addContentViewAnimations:]):
* UIProcess/ios/forms/WKQuickboardListViewController.h: Added.
* UIProcess/ios/forms/WKQuickboardListViewController.mm: Added.
(-[WKQuickboardListItemCell topToLabelBaselineSpecValue]):
(-[WKQuickboardListItemCell baselineToBottomSpecValue]):
(-[WKQuickboardListViewController initWithDelegate:]):
(-[WKQuickboardListViewController updateContextViewIfNeeded]):
(-[WKQuickboardListViewController prefersStatusBarHidden]):
(-[WKQuickboardListViewController viewDidLoad]):
(-[WKQuickboardListViewController viewWillAppear:]):
(-[WKQuickboardListViewController viewDidDisappear:]):
(-[WKQuickboardListViewController _handleStatusBarNavigation]):
(-[WKQuickboardListViewController reloadContextView]):
(-[WKQuickboardListViewController actionController]):
(-[WKQuickboardListViewController languageControllerDidChangePrimaryLanguage:]):
(-[WKQuickboardListViewController headerContentViewHeight]):
(-[WKQuickboardListViewController headerContentView]):
(configureStatusBarForController):
* UIProcess/ios/forms/WKSelectMenuListViewController.h:
* UIProcess/ios/forms/WKSelectMenuListViewController.mm:
(-[WKSelectMenuItemCell initWithStyle:reuseIdentifier:]):
(-[WKSelectMenuItemCell imageView]):
(-[WKSelectMenuListViewController initWithDelegate:]):
(-[WKSelectMenuListViewController viewDidLoad]):
(-[WKSelectMenuListViewController acceptButtonTappedWithCompletion:]):
(-[WKSelectMenuListViewController shouldShowTrayView]):
(-[WKSelectMenuListViewController didSelectListItem:]):
(-[WKSelectMenuListViewController numberOfListItems]):
(-[WKSelectMenuListViewController heightForListItem:width:]):
(-[WKSelectMenuListViewController cellForListItem:]):
(-[WKSelectMenuListViewController selectItemAtIndex:]):
* UIProcess/ios/forms/WKTimePickerViewController.h:
* UIProcess/ios/forms/WKTimePickerViewController.mm:
(-[WKTimePickerViewController initWithDelegate:]):
(-[WKTimePickerViewController dateFormatter]):
(-[WKTimePickerViewController timeValueForFormControls]):
(-[WKTimePickerViewController dateComponentsFromInitialValue]):
(-[WKTimePickerViewController viewDidAppear:]):
(-[WKTimePickerViewController viewDidLoad]):
(-[WKTimePickerViewController becomeFirstResponder]):
(-[WKTimePickerViewController setHour:minute:]):
(-[WKTimePickerViewController leftButtonWOTAction]):
(-[WKTimePickerViewController rightButtonWOTAction]):
* WebKit.xcodeproj/project.pbxproj:

LayoutTests:

Upstream a couple of internal test expectations on watchOS.

* fast/viewport/extrazoom/viewport-adaptations-after-navigation-expected.txt: Added.
* fast/viewport/extrazoom/viewport-disable-extra-zoom-adaptations-expected.txt: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@232770 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/viewport/extrazoom/viewport-adaptations-after-navigation-expected.txt [new file with mode: 0644]
LayoutTests/fast/viewport/extrazoom/viewport-disable-extra-zoom-adaptations-expected.txt [new file with mode: 0644]
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/ios/forms/WKDatePickerViewController.h
Source/WebKit/UIProcess/ios/forms/WKDatePickerViewController.mm
Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.h [deleted file]
Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.mm [deleted file]
Source/WebKit/UIProcess/ios/forms/WKNumberPadViewController.h
Source/WebKit/UIProcess/ios/forms/WKNumberPadViewController.mm
Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.h [new file with mode: 0644]
Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.mm [new file with mode: 0644]
Source/WebKit/UIProcess/ios/forms/WKSelectMenuListViewController.h
Source/WebKit/UIProcess/ios/forms/WKSelectMenuListViewController.mm
Source/WebKit/UIProcess/ios/forms/WKTimePickerViewController.h
Source/WebKit/UIProcess/ios/forms/WKTimePickerViewController.mm
Source/WebKit/WebKit.xcodeproj/project.pbxproj

index c254f3b..6b69583 100644 (file)
@@ -1,5 +1,18 @@
 2018-06-12  Wenson Hsieh  <wenson_hsieh@apple.com>
 
+        [WebKit on watchOS] Upstream watchOS source additions to OpenSource (Part 2)
+        https://bugs.webkit.org/show_bug.cgi?id=186442
+        <rdar://problem/40879364>
+
+        Reviewed by Tim Horton.
+
+        Upstream a couple of internal test expectations on watchOS.
+
+        * fast/viewport/extrazoom/viewport-adaptations-after-navigation-expected.txt: Added.
+        * fast/viewport/extrazoom/viewport-disable-extra-zoom-adaptations-expected.txt: Added.
+
+2018-06-12  Wenson Hsieh  <wenson_hsieh@apple.com>
+
         REGRESSION(r228724): Occasional crash when executing ReplaceSelectionCommand at the end of the document
         https://bugs.webkit.org/show_bug.cgi?id=186555
         <rdar://problem/39703004>
diff --git a/LayoutTests/fast/viewport/extrazoom/viewport-adaptations-after-navigation-expected.txt b/LayoutTests/fast/viewport/extrazoom/viewport-adaptations-after-navigation-expected.txt
new file mode 100644 (file)
index 0000000..7fba6dc
--- /dev/null
@@ -0,0 +1 @@
+previous size: (156, 175); current size: (320, 359)
diff --git a/LayoutTests/fast/viewport/extrazoom/viewport-disable-extra-zoom-adaptations-expected.txt b/LayoutTests/fast/viewport/extrazoom/viewport-disable-extra-zoom-adaptations-expected.txt
new file mode 100644 (file)
index 0000000..21eb611
--- /dev/null
@@ -0,0 +1,22 @@
+PASS scaleAtDeviceWidthWithDefaultAdaptations is '0.488'
+PASS scaleAtDeviceWidthWithAdaptationDisabled is '1.000'
+PASS scaleAtDeviceWidthWithShrinkToFitDisabled is '0.488'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+1. Default viewport
+[width=150] (150, 168)
+[width=device-width] (320, 359)
+[width=600] (600, 673)
+
+2. extra zoom mode adaptations disabled
+[width=150; one,       watch   ,two] (150, 168)
+[width=device-width; watch, three, four, watch] (156, 175)
+[width=600; five, watch] (600, 673)
+
+3. shrink-to-fit disabled
+[width=150, shrink-to-fit=no; ] (150, 168)
+[width=device-width, shrink-to-fit=0; bogus, values] (320, 359)
+[width=600, shrink-to-fit=-0.5; ,,,] (600, 673)
index fd6a2c5..ae68efe 100644 (file)
@@ -1,3 +1,133 @@
+2018-06-12  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [WebKit on watchOS] Upstream watchOS source additions to OpenSource (Part 2)
+        https://bugs.webkit.org/show_bug.cgi?id=186442
+        <rdar://problem/40879364>
+
+        Reviewed by Tim Horton.
+
+        Upstream most of the work around form controls on watchOS. Also, rename WKFormControlListViewController.* to
+        its intended name, WKQuickboardListViewController.*.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        * UIProcess/ios/forms/WKDatePickerViewController.h:
+        * UIProcess/ios/forms/WKDatePickerViewController.mm:
+        (datePickerSetButtonHeight):
+        (datePickerVerticalMargin):
+        (-[WKDatePickerWheelLabel initWithFrame:]):
+        (-[WKDatePickerWheelLabel lastSelectedDate]):
+        (-[WKDatePickerWheelLabel setLastSelectedDate:]):
+        (-[WKDatePickerWheelLabel needsUpdateForIndex:selectedDate:]):
+        (-[WKDatePickerWheel initWithStyle:]):
+        (-[WKDatePickerWheel initWithController:style:]):
+        (-[WKDatePickerWheel gestureRecognized:]):
+        (-[WKDatePickerWheel setDrawsFocusOutline:]):
+        (-[WKDatePickerWheel drawsFocusOutline]):
+        (-[WKDatePickerWheel gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
+        (-[WKDatePickerViewController initWithDelegate:]):
+        (-[WKDatePickerViewController viewDidLoad]):
+        (-[WKDatePickerViewController prefersStatusBarHidden]):
+        (-[WKDatePickerViewController viewWillAppear:]):
+        (-[WKDatePickerViewController viewDidAppear:]):
+        (-[WKDatePickerViewController viewDidDisappear:]):
+        (-[WKDatePickerViewController _handleStatusBarNavigation]):
+        (-[WKDatePickerViewController viewWillLayoutSubviews]):
+        (-[WKDatePickerViewController becomeFirstResponder]):
+        (-[WKDatePickerViewController defaultMinimumDate]):
+        (-[WKDatePickerViewController defaultMaximumDate]):
+        (-[WKDatePickerViewController _valueForInput]):
+        (-[WKDatePickerViewController _dateFromInitialText]):
+        (-[WKDatePickerViewController _setButtonPressed]):
+        (-[WKDatePickerViewController _updateSelectedPickerViewIndices]):
+        (-[WKDatePickerViewController _configurePickerView:]):
+        (-[WKDatePickerViewController setMinimumDate:]):
+        (-[WKDatePickerViewController minimumDate]):
+        (-[WKDatePickerViewController setMaximumDate:]):
+        (-[WKDatePickerViewController maximumDate]):
+        (-[WKDatePickerViewController setDate:]):
+        (-[WKDatePickerViewController setDateFromComponents:]):
+        (-[WKDatePickerViewController setDay:month:year:era:]):
+        (-[WKDatePickerViewController date]):
+        (-[WKDatePickerViewController _dateComponentForDay:month:year:era:]):
+        (-[WKDatePickerViewController _adjustDateToValidDateIfNecessary]):
+        (-[WKDatePickerViewController _createAndConfigureGranularityLabelWithText:]):
+        (-[WKDatePickerViewController _canonicalizeAndUpdateSelectedDate]):
+        (-[WKDatePickerViewController numberOfItemsInPickerView:]):
+        (-[WKDatePickerViewController pickerView:viewForItemAtIndex:]):
+        (-[WKDatePickerViewController didBeginInteractingWithDatePicker:]):
+        (-[WKDatePickerViewController pickerView:didSelectItemAtIndex:]):
+        (-[WKDatePickerViewController pickerViewWillBeginSelection:]):
+        (-[WKDatePickerViewController pickerViewDidEndSelection:]):
+        (-[WKDatePickerViewController _dayFromIndex:]):
+        (-[WKDatePickerViewController _eraAndYearFromIndex:]):
+        (-[WKDatePickerViewController _monthFromIndex:]):
+        (-[WKDatePickerViewController _indexFromDay:]):
+        (-[WKDatePickerViewController _indexFromYear:era:]):
+        (-[WKDatePickerViewController _indexFromMonth:]):
+        * UIProcess/ios/forms/WKFormControlListViewController.h: Removed.
+        * UIProcess/ios/forms/WKFormControlListViewController.mm: Removed.
+        * UIProcess/ios/forms/WKNumberPadViewController.h:
+        * UIProcess/ios/forms/WKNumberPadViewController.mm:
+        (inputLabelFontSize):
+        (-[WKNumberPadViewController initWithDelegate:initialText:inputMode:]):
+        (-[WKNumberPadViewController dealloc]):
+        (-[WKNumberPadViewController viewDidLoad]):
+        (-[WKNumberPadViewController viewWillDisappear:]):
+        (-[WKNumberPadViewController viewWillLayoutSubviews]):
+        (-[WKNumberPadViewController _reloadHeaderViewFromInputText]):
+        (-[WKNumberPadViewController didSelectKey:]):
+        (-[WKNumberPadViewController _handleKeyPress:]):
+        (-[WKNumberPadViewController _cancelInput]):
+        (-[WKNumberPadViewController _deleteLastInputCharacter]):
+        (-[WKNumberPadViewController _deleteButtonPressed]):
+        (-[WKNumberPadViewController _cancelDeletionTimers]):
+        (-[WKNumberPadViewController _startDeletionTimer]):
+        (-[WKNumberPadViewController _deletionTimerFired]):
+        (-[WKNumberPadViewController addContentViewAnimations:]):
+        * UIProcess/ios/forms/WKQuickboardListViewController.h: Added.
+        * UIProcess/ios/forms/WKQuickboardListViewController.mm: Added.
+        (-[WKQuickboardListItemCell topToLabelBaselineSpecValue]):
+        (-[WKQuickboardListItemCell baselineToBottomSpecValue]):
+        (-[WKQuickboardListViewController initWithDelegate:]):
+        (-[WKQuickboardListViewController updateContextViewIfNeeded]):
+        (-[WKQuickboardListViewController prefersStatusBarHidden]):
+        (-[WKQuickboardListViewController viewDidLoad]):
+        (-[WKQuickboardListViewController viewWillAppear:]):
+        (-[WKQuickboardListViewController viewDidDisappear:]):
+        (-[WKQuickboardListViewController _handleStatusBarNavigation]):
+        (-[WKQuickboardListViewController reloadContextView]):
+        (-[WKQuickboardListViewController actionController]):
+        (-[WKQuickboardListViewController languageControllerDidChangePrimaryLanguage:]):
+        (-[WKQuickboardListViewController headerContentViewHeight]):
+        (-[WKQuickboardListViewController headerContentView]):
+        (configureStatusBarForController):
+        * UIProcess/ios/forms/WKSelectMenuListViewController.h:
+        * UIProcess/ios/forms/WKSelectMenuListViewController.mm:
+        (-[WKSelectMenuItemCell initWithStyle:reuseIdentifier:]):
+        (-[WKSelectMenuItemCell imageView]):
+        (-[WKSelectMenuListViewController initWithDelegate:]):
+        (-[WKSelectMenuListViewController viewDidLoad]):
+        (-[WKSelectMenuListViewController acceptButtonTappedWithCompletion:]):
+        (-[WKSelectMenuListViewController shouldShowTrayView]):
+        (-[WKSelectMenuListViewController didSelectListItem:]):
+        (-[WKSelectMenuListViewController numberOfListItems]):
+        (-[WKSelectMenuListViewController heightForListItem:width:]):
+        (-[WKSelectMenuListViewController cellForListItem:]):
+        (-[WKSelectMenuListViewController selectItemAtIndex:]):
+        * UIProcess/ios/forms/WKTimePickerViewController.h:
+        * UIProcess/ios/forms/WKTimePickerViewController.mm:
+        (-[WKTimePickerViewController initWithDelegate:]):
+        (-[WKTimePickerViewController dateFormatter]):
+        (-[WKTimePickerViewController timeValueForFormControls]):
+        (-[WKTimePickerViewController dateComponentsFromInitialValue]):
+        (-[WKTimePickerViewController viewDidAppear:]):
+        (-[WKTimePickerViewController viewDidLoad]):
+        (-[WKTimePickerViewController becomeFirstResponder]):
+        (-[WKTimePickerViewController setHour:minute:]):
+        (-[WKTimePickerViewController leftButtonWOTAction]):
+        (-[WKTimePickerViewController rightButtonWOTAction]):
+        * WebKit.xcodeproj/project.pbxproj:
+
 2018-06-12  Jer Noble  <jer.noble@apple.com>
 
         Make Modern EME An Experimental Feature Again
index c613deb..049006f 100644 (file)
@@ -43,7 +43,6 @@
 #import "WKDatePickerViewController.h"
 #import "WKError.h"
 #import "WKFocusedFormControlView.h"
-#import "WKFormControlListViewController.h"
 #import "WKFormInputControl.h"
 #import "WKFormSelectControl.h"
 #import "WKImagePreviewViewController.h"
@@ -52,6 +51,7 @@
 #import "WKPreviewActionItemIdentifiers.h"
 #import "WKPreviewActionItemInternal.h"
 #import "WKPreviewElementInfoInternal.h"
+#import "WKQuickboardListViewController.h"
 #import "WKSelectMenuListViewController.h"
 #import "WKTextInputListViewController.h"
 #import "WKTimePickerViewController.h"
index 8e47642..0f6664f 100644 (file)
 
 #pragma once
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKDatePickerViewControllerAdditions.h>
+#if PLATFORM(WATCHOS)
+
+#import "WKQuickboardListViewController.h"
+
+@interface WKDatePickerViewController : PUICQuickboardViewController
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
+
+@property (nonatomic, weak) id <WKQuickboardViewControllerDelegate> delegate;
+
+@end
+
 #endif
index 9964e18..b87cbf5 100644 (file)
 #include "config.h"
 #include "WKDatePickerViewController.h"
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKDatePickerViewControllerAdditions.mm>
+#if PLATFORM(WATCHOS)
+
+#import <PepperUICore/PUICApplication_Private.h>
+#import <PepperUICore/PUICPickerView.h>
+#import <PepperUICore/PUICPickerView_Private.h>
+#import <PepperUICore/PUICQuickboardViewController_Private.h>
+#import <PepperUICore/PUICStatusBar_Private.h>
+#import <PepperUICore/UIDevice+PUICAdditions.h>
+#import <UIKit/UIGeometry_Private.h>
+#import <UIKit/UIInterface_Private.h>
+#import <UIKit/UIResponder_Private.h>
+#import <UIKit/UIView_Private.h>
+#import <WebCore/LocalizedStrings.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/WeakObjCPtr.h>
+#import <wtf/text/WTFString.h>
+
+static const CGFloat pickerViewHorizontalMargin = 4;
+static const CGFloat pickerViewBorderRadius = 6;
+static const CGFloat pickerViewUnfocusedBorderWidth = 1;
+static const CGFloat pickerViewFocusedBorderWidth = 2;
+static const CGFloat pickerViewGranularityLabelBorderRadius = 8;
+static const CGFloat pickerViewGranularityLabelFontSize = 10;
+static const CGFloat pickerViewGranularityLabelHeight = 16;
+static const CGFloat pickerViewGranularityLabelHorizontalPadding = 12;
+static const CGFloat pickerViewGranularityLabelBottomMargin = 2;
+static const CGFloat datePickerSetButtonTitleFontSize = 16;
+
+static CGFloat datePickerSetButtonHeight()
+{
+    return [[UIDevice currentDevice] puic_deviceVariant] == PUICDeviceVariantCompact ? 32 : 40;
+}
+
+static CGFloat datePickerVerticalMargin()
+{
+    return [[UIDevice currentDevice] puic_deviceVariant] == PUICDeviceVariantCompact ? 10 : 14;
+}
+
+static NSString *pickerViewStandaloneYearFormat = @"y";
+static NSString *pickerViewStandaloneDateFormat = @"dd";
+static NSString *pickerViewStandaloneMonthFormat = @"MMM";
+static NSString *inputValueDateFormat = @"yyyy-MM-dd";
+
+#pragma mark - WKDatePickerWheelLabel
+
+@interface WKDatePickerWheelLabel : UILabel
+
+- (BOOL)needsUpdateForIndex:(NSUInteger)index selectedDate:(NSDate *)date;
+@property (nonatomic) NSUInteger index;
+@property (nonatomic, copy) NSDate *lastSelectedDate;
+
+@end
+
+@implementation WKDatePickerWheelLabel {
+    RetainPtr<NSDate> _lastSelectedDate;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+    if (!(self = [super initWithFrame:frame]))
+        return nil;
+
+    self.index = NSNotFound;
+    self.lastSelectedDate = nil;
+    self.textColor = [UIColor whiteColor];
+    self.textAlignment = NSTextAlignmentCenter;
+    self.opaque = YES;
+    self.adjustsFontSizeToFitWidth = YES;
+    return self;
+}
+
+- (NSDate *)lastSelectedDate
+{
+    return _lastSelectedDate.get();
+}
+
+- (void)setLastSelectedDate:(NSDate *)date
+{
+    _lastSelectedDate = adoptNS([date copy]);
+}
+
+- (BOOL)needsUpdateForIndex:(NSUInteger)index selectedDate:(NSDate *)date
+{
+    return self.index != index || ![self.lastSelectedDate isEqualToDate:date];
+}
+
+@end
+
+#pragma mark - WKDatePickerWheel
+
+@interface WKDatePickerWheel : PUICPickerView
+- (instancetype)initWithController:(WKDatePickerViewController *)controller style:(PUICPickerViewStyle)style NS_DESIGNATED_INITIALIZER;
+@property (nonatomic) BOOL drawsFocusOutline;
+@property (nonatomic, getter=isChangingValue) BOOL changingValue;
+@end
+
+@interface WKDatePickerViewController () <PUICPickerViewDataSource, PUICPickerViewDelegate>
+@property (nonatomic, copy) NSDate *date;
+@property (nonatomic, copy) NSDate *minimumDate;
+@property (nonatomic, copy) NSDate *maximumDate;
+- (void)didBeginInteractingWithDatePicker:(WKDatePickerWheel *)datePicker;
+@end
+
+@interface WKDatePickerWheel () <UIGestureRecognizerDelegate>
+@end
+
+@implementation WKDatePickerWheel {
+    WeakObjCPtr<WKDatePickerViewController> _controller;
+    RetainPtr<UILongPressGestureRecognizer> _gesture;
+    BOOL _drawsFocusOutline;
+}
+
+- (instancetype)initWithStyle:(PUICPickerViewStyle)style
+{
+    return [self initWithController:nil style:style];
+}
+
+- (instancetype)initWithController:(WKDatePickerViewController *)controller style:(PUICPickerViewStyle)style
+{
+    if (!(self = [super initWithStyle:style]))
+        return nil;
+
+    self._continuousCornerRadius = pickerViewBorderRadius;
+    self.layer.borderColor = [UIColor systemGrayColor].CGColor;
+    self.layer.borderWidth = pickerViewUnfocusedBorderWidth;
+    self.drawsFocusOutline = NO;
+    self.changingValue = NO;
+
+    _controller = controller;
+    _gesture = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)]);
+    [_gesture setDelegate:self];
+    [_gesture setMinimumPressDuration:0];
+    [self addGestureRecognizer:_gesture.get()];
+
+    return self;
+}
+
+- (void)gestureRecognized:(UIGestureRecognizer *)gesture
+{
+    if (gesture.state == UIGestureRecognizerStateBegan)
+        [_controller didBeginInteractingWithDatePicker:self];
+}
+
+- (void)setDrawsFocusOutline:(BOOL)drawsFocusOutline
+{
+    if (_drawsFocusOutline == drawsFocusOutline)
+        return;
+
+    _drawsFocusOutline = drawsFocusOutline;
+    self.layer.borderColor = (_drawsFocusOutline ? [UIColor systemGreenColor] : [UIColor systemGrayColor]).CGColor;
+    self.layer.borderWidth = _drawsFocusOutline ? pickerViewFocusedBorderWidth : pickerViewUnfocusedBorderWidth;
+}
+
+- (BOOL)drawsFocusOutline
+{
+    return _drawsFocusOutline;
+}
+
+#pragma mark - UIGestureRecognizerDelegate
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+    return YES;
+}
+
+@end
+
+struct EraAndYear {
+    NSInteger era;
+    NSInteger year;
+};
+
+@implementation WKDatePickerViewController {
+    NSInteger _day;
+    NSInteger _month;
+    NSInteger _year;
+    NSInteger _era;
+
+    RetainPtr<WKDatePickerWheel> _monthPicker;
+    RetainPtr<WKDatePickerWheel> _dayPicker;
+    RetainPtr<WKDatePickerWheel> _yearAndEraPicker;
+    RetainPtr<WKDatePickerWheel> _focusedPicker;
+    RetainPtr<UILabel> _monthLabel;
+    RetainPtr<UILabel> _dayLabel;
+    RetainPtr<UILabel> _yearLabel;
+    RetainPtr<UIButton> _setButton;
+
+    RetainPtr<NSTimeZone> _timeZoneForDateFormatting;
+    RetainPtr<NSCalendar> _calendar;
+
+    RetainPtr<NSDate> _minimumDate;
+    RetainPtr<NSDate> _maximumDate;
+    RetainPtr<NSDate> _cachedDate;
+
+    BOOL _displayCombinedEraAndYear;
+    RetainPtr<NSDateFormatter> _standaloneYearFormatter;
+    RetainPtr<NSDateFormatter> _standaloneDayFormatter;
+    RetainPtr<NSDateFormatter> _standaloneMonthFormatter;
+    RetainPtr<NSDateFormatter> _standaloneInputValueFormatter;
+
+    RetainPtr<PUICStatusBarGlobalContextViewAssertion> _statusBarAssertion;
+}
+
+@dynamic delegate;
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate
+{
+    return [super initWithDelegate:delegate];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+
+    self.headerView.hidden = YES;
+
+    NSString *localeIdentifier = [NSLocale currentLocale].localeIdentifier;
+    _displayCombinedEraAndYear = [localeIdentifier containsString:@"calendar=japanese"] || [localeIdentifier isEqualToString:@"ja_JP_TRADITIONAL"];
+    _timeZoneForDateFormatting = [NSTimeZone localTimeZone];
+    _calendar = [NSCalendar currentCalendar];
+    [_calendar setTimeZone:_timeZoneForDateFormatting.get()];
+
+    _standaloneYearFormatter = adoptNS([[NSDateFormatter alloc] init]);
+    [_standaloneYearFormatter setDateFormat:pickerViewStandaloneYearFormat];
+    [_standaloneYearFormatter setLocale:NSLocale.currentLocale];
+    [_standaloneYearFormatter setCalendar:_calendar.get()];
+    [_standaloneYearFormatter setTimeZone:_timeZoneForDateFormatting.get()];
+
+    _standaloneDayFormatter = adoptNS([[NSDateFormatter alloc] init]);
+    [_standaloneDayFormatter setDateFormat:[NSDateFormatter dateFormatFromTemplate:pickerViewStandaloneDateFormat options:0 locale:NSLocale.currentLocale]];
+    [_standaloneDayFormatter setCalendar:_calendar.get()];
+    [_standaloneDayFormatter setTimeZone:_timeZoneForDateFormatting.get()];
+
+    _standaloneMonthFormatter = adoptNS([[NSDateFormatter alloc] init]);
+    [_standaloneMonthFormatter setDateFormat:[NSDateFormatter dateFormatFromTemplate:pickerViewStandaloneMonthFormat options:0 locale:NSLocale.currentLocale]];
+    [_standaloneMonthFormatter setCalendar:_calendar.get()];
+    [_standaloneMonthFormatter setTimeZone:_timeZoneForDateFormatting.get()];
+
+    _standaloneInputValueFormatter = adoptNS([[NSDateFormatter alloc] init]);
+    [_standaloneInputValueFormatter setDateFormat:inputValueDateFormat];
+    [_standaloneInputValueFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];
+    [_standaloneInputValueFormatter setTimeZone:_timeZoneForDateFormatting.get()];
+
+    self.minimumDate = self.defaultMinimumDate;
+    self.maximumDate = self.defaultMaximumDate;
+    self.date = [self _dateFromInitialText] ?: [NSDate date];
+
+    _monthPicker = adoptNS([[WKDatePickerWheel alloc] initWithController:self style:PUICPickerViewStyleList]);
+    [self _configurePickerView:_monthPicker.get()];
+    _monthLabel = [self _createAndConfigureGranularityLabelWithText:WebCore::datePickerMonthLabelTitle()];
+
+    _dayPicker = adoptNS([[WKDatePickerWheel alloc] initWithController:self style:PUICPickerViewStyleList]);
+    [self _configurePickerView:_dayPicker.get()];
+    _dayLabel = [self _createAndConfigureGranularityLabelWithText:WebCore::datePickerDayLabelTitle()];
+
+    _yearAndEraPicker = adoptNS([[WKDatePickerWheel alloc] initWithController:self style:PUICPickerViewStyleList]);
+    [self _configurePickerView:_yearAndEraPicker.get()];
+    _yearLabel = [self _createAndConfigureGranularityLabelWithText:WebCore::datePickerYearLabelTitle()];
+
+    [self _updateSelectedPickerViewIndices];
+
+    [_dayPicker setDrawsFocusOutline:YES];
+    [_dayLabel setHidden:NO];
+    _focusedPicker = _dayPicker;
+
+    _setButton = [UIButton buttonWithType:UIButtonTypeSystem];
+    [_setButton setTitle:WebCore::datePickerSetButtonTitle() forState:UIControlStateNormal];
+    [_setButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
+    [_setButton setBackgroundColor:[UIColor systemGreenColor]];
+    [_setButton _setContinuousCornerRadius:datePickerSetButtonHeight() / 2];
+    [_setButton titleLabel].font = [UIFont systemFontOfSize:datePickerSetButtonTitleFontSize];
+    [_setButton addTarget:self action:@selector(_setButtonPressed) forControlEvents:UIControlEventTouchUpInside];
+    [self.contentView addSubview:_setButton.get()];
+
+    self.view.opaque = YES;
+    self.view.backgroundColor = [UIColor blackColor];
+}
+
+- (BOOL)prefersStatusBarHidden
+{
+    return NO;
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+    [super viewWillAppear:animated];
+
+    _statusBarAssertion = [[PUICApplication sharedPUICApplication] _takeStatusBarGlobalContextAssertionAnimated:NO];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleStatusBarNavigation) name:PUICStatusBarNavigationBackButtonPressedNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleStatusBarNavigation) name:PUICStatusBarTitleTappedNotification object:nil];
+
+    configureStatusBarForController(self, self.delegate);
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+    [super viewDidAppear:animated];
+    [self becomeFirstResponder];
+}
+
+- (void)viewDidDisappear:(BOOL)animated
+{
+    [super viewDidDisappear:animated];
+
+    _statusBarAssertion = nil;
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)_handleStatusBarNavigation
+{
+    [self.delegate quickboardInputCancelled:self];
+}
+
+- (void)viewWillLayoutSubviews
+{
+    [super viewWillLayoutSubviews];
+
+    auto viewBounds = self.view.bounds;
+    auto width = CGRectGetWidth(viewBounds);
+    auto height = CGRectGetHeight(viewBounds);
+
+    [_setButton setFrame:CGRectMake(0, height - datePickerSetButtonHeight(), width, datePickerSetButtonHeight())];
+
+    [_monthLabel sizeToFit];
+    [_dayLabel sizeToFit];
+    [_yearLabel sizeToFit];
+
+    CGRect pickerLayoutFrame = CGRectMake(0, CGRectGetMaxY(self.headerView.frame), width, CGRectGetMinY([_setButton frame]) - CGRectGetMaxY(self.headerView.frame));
+    pickerLayoutFrame = UIRectInsetEdges(pickerLayoutFrame, UIRectEdgeTop | UIRectEdgeBottom, datePickerVerticalMargin());
+    CGFloat pickerViewLayoutOriginY = CGRectGetMinY(pickerLayoutFrame) + pickerViewGranularityLabelHeight + pickerViewGranularityLabelBottomMargin;
+    CGFloat pickerViewColumnWidth = CGRectGetWidth(pickerLayoutFrame) / 3;
+    CGFloat pickerViewLayoutHeight = CGRectGetHeight(pickerLayoutFrame) -  pickerViewGranularityLabelHeight - pickerViewGranularityLabelBottomMargin;
+
+    // FIXME: Respect locale and interface direction when mapping picker views and labels to left, middle and right positions.
+    WKDatePickerWheel *leftPicker = _monthPicker.get();
+    WKDatePickerWheel *middlePicker = _dayPicker.get();
+    WKDatePickerWheel *rightPicker = _yearAndEraPicker.get();
+    UILabel *leftLabel = _monthLabel.get();
+    UILabel *middleLabel = _dayLabel.get();
+    UILabel *rightLabel = _yearLabel.get();
+
+    leftPicker.frame = UIRectInsetEdges(CGRectMake(0, pickerViewLayoutOriginY, pickerViewColumnWidth, pickerViewLayoutHeight), UIRectEdgeRight, pickerViewHorizontalMargin);
+    leftLabel.frame = UIRectInsetEdges(CGRectMake(0, CGRectGetMinY(pickerLayoutFrame), CGRectGetWidth(leftLabel.bounds), pickerViewGranularityLabelHeight), UIRectEdgeRight, -pickerViewGranularityLabelHorizontalPadding);
+
+    middlePicker.frame = UIRectInsetEdges(CGRectMake(pickerViewColumnWidth, pickerViewLayoutOriginY, pickerViewColumnWidth, pickerViewLayoutHeight), UIRectEdgeRight | UIRectEdgeLeft, pickerViewHorizontalMargin / 2);
+    middleLabel.frame = UIRectInsetEdges(CGRectMake((CGRectGetWidth(pickerLayoutFrame) - CGRectGetWidth(middleLabel.bounds)) / 2, CGRectGetMinY(pickerLayoutFrame), CGRectGetWidth(middleLabel.bounds), pickerViewGranularityLabelHeight), UIRectEdgeRight | UIRectEdgeLeft, -pickerViewGranularityLabelHorizontalPadding / 2);
+
+    rightPicker.frame = UIRectInsetEdges(CGRectMake(2 * pickerViewColumnWidth, pickerViewLayoutOriginY, pickerViewColumnWidth, pickerViewLayoutHeight), UIRectEdgeLeft, pickerViewHorizontalMargin);
+    rightLabel.frame = UIRectInsetEdges(CGRectMake(CGRectGetWidth(pickerLayoutFrame) - CGRectGetWidth(rightLabel.bounds), CGRectGetMinY(pickerLayoutFrame), CGRectGetWidth(rightLabel.bounds), pickerViewGranularityLabelHeight), UIRectEdgeLeft, -pickerViewGranularityLabelHorizontalPadding);
+}
+
+- (BOOL)becomeFirstResponder
+{
+    return [_focusedPicker becomeFirstResponder];
+}
+
+- (NSDate *)defaultMinimumDate
+{
+    auto components = adoptNS([[NSDateComponents alloc] init]);
+    [components setDay:1];
+    [components setMonth:1];
+    [components setYear:1];
+    NSDate *minDateInGregorianCalendar = [[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian] dateFromComponents:components.get()];
+    NSDate *minDateInCurrentCalendar = nil;
+    if (![_calendar rangeOfUnit:NSCalendarUnitYear startDate:&minDateInCurrentCalendar interval:nil forDate:minDateInGregorianCalendar]) {
+        // Fall back to the Gregorian calendar date if the year containing the min date couldn't be computed using the current calendar.
+        ASSERT_NOT_REACHED();
+        return minDateInGregorianCalendar;
+    }
+
+    if ([_calendar compareDate:minDateInCurrentCalendar toDate:minDateInGregorianCalendar toUnitGranularity:NSCalendarUnitDay] == NSOrderedAscending)
+        minDateInCurrentCalendar = [_calendar dateByAddingUnit:NSCalendarUnitYear value:1 toDate:minDateInCurrentCalendar options:0];
+
+    return minDateInCurrentCalendar;
+}
+
+- (NSDate *)defaultMaximumDate
+{
+    auto components = adoptNS([[NSDateComponents alloc] init]);
+    [components setDay:1];
+    [components setMonth:1];
+    [components setYear:10000];
+    NSDate *dayAfterMaxDateInGregorianCalendar = [[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian] dateFromComponents:components.get()];
+    NSDate *dayAfterMaxDateInCurrentCalendar = nil;
+    NSDate *dayAfterMaxDate = nil;
+    if ([_calendar rangeOfUnit:NSCalendarUnitYear startDate:&dayAfterMaxDateInCurrentCalendar interval:nil forDate:dayAfterMaxDateInGregorianCalendar])
+        dayAfterMaxDate = dayAfterMaxDateInCurrentCalendar;
+    else {
+        // Fall back to the Gregorian calendar date if the year containing the max date couldn't be computed using the current calendar.
+        ASSERT_NOT_REACHED();
+        dayAfterMaxDate = dayAfterMaxDateInGregorianCalendar;
+    }
+    return [_calendar dateByAddingUnit:NSCalendarUnitDay value:-1 toDate:dayAfterMaxDate options:0];
+}
+
+- (NSString *)_valueForInput
+{
+    return [_standaloneInputValueFormatter stringFromDate:self.date];
+}
+
+- (NSDate *)_dateFromInitialText
+{
+    return [_standaloneInputValueFormatter dateFromString:[self.delegate initialValueForViewController:self]];
+}
+
+- (void)_setButtonPressed
+{
+    [self _canonicalizeAndUpdateSelectedDate];
+
+    auto attributedStringValue = adoptNS([[NSAttributedString alloc] initWithString:[self _valueForInput]]);
+    [self.delegate quickboard:self textEntered:attributedStringValue.get()];
+}
+
+- (void)_updateSelectedPickerViewIndices
+{
+    [_monthPicker setSelectedIndex:[self _indexFromMonth:_month]];
+    [_dayPicker setSelectedIndex:[self _indexFromDay:_day]];
+    [_yearAndEraPicker setSelectedIndex:[self _indexFromYear:_year era:_era]];
+}
+
+- (void)_configurePickerView:(WKDatePickerWheel *)picker
+{
+    picker.dataSource = self;
+    picker.delegate = self;
+    [self.contentView addSubview:picker];
+}
+
+- (void)setMinimumDate:(NSDate *)date
+{
+    if ([_minimumDate isEqualToDate:date])
+        return;
+
+    _minimumDate = adoptNS([date copy]);
+    [_monthPicker reloadData];
+    [_yearAndEraPicker reloadData];
+    [_dayPicker reloadData];
+}
+
+- (NSDate *)minimumDate
+{
+    return _minimumDate.get();
+}
+
+- (void)setMaximumDate:(NSDate *)date
+{
+    if ([_minimumDate isEqualToDate:date])
+        return;
+
+    _maximumDate = adoptNS([date copy]);
+    [_monthPicker reloadData];
+    [_yearAndEraPicker reloadData];
+    [_dayPicker reloadData];
+}
+
+- (NSDate *)maximumDate
+{
+    return _maximumDate.get();
+}
+
+- (void)setDate:(NSDate *)date
+{
+    if ([self.date isEqualToDate:date])
+        return;
+
+    if ([date compare:_minimumDate.get()] == NSOrderedAscending)
+        date = _minimumDate.get();
+
+    if ([date compare:_maximumDate.get()] == NSOrderedDescending)
+        date = _maximumDate.get();
+
+    [self setDateFromComponents:[_calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitEra fromDate:date]];
+}
+
+- (void)setDateFromComponents:(NSDateComponents *)components
+{
+    [self setDay:components.day month:components.month year:components.year era:components.era];
+}
+
+- (void)setDay:(NSInteger)day month:(NSInteger)month year:(NSInteger)year era:(NSInteger)era
+{
+    _day = day;
+    _month = month;
+    _year = year;
+    _era = era;
+    _cachedDate = nil;
+}
+
+- (NSDate *)date
+{
+    if (!_cachedDate)
+        _cachedDate = [self _dateComponentForDay:_day month:_month year:_year era:_era].date;
+    return _cachedDate.get();
+}
+
+- (NSDateComponents *)_dateComponentForDay:(NSInteger)day month:(NSInteger)month year:(NSInteger)year era:(NSInteger)era
+{
+    NSDateComponents *dateComponents = [[[NSDateComponents alloc] init] autorelease];
+    dateComponents.day = day;
+    dateComponents.month = month;
+    dateComponents.year = year;
+    dateComponents.era = era;
+    dateComponents.calendar = _calendar.get();
+    return dateComponents;
+}
+
+- (void)_adjustDateToValidDateIfNecessary
+{
+    NSDateComponents *adjustedComponents = [self _dateComponentForDay:_day month:_month year:_year era:_era];
+    BOOL didAdjust = NO;
+    while (![adjustedComponents isValidDateInCalendar:_calendar.get()]) {
+        if (adjustedComponents.day <= 1) {
+            ASSERT_NOT_REACHED();
+            break;
+        }
+        --adjustedComponents.day;
+        didAdjust = YES;
+    }
+
+    [self setDateFromComponents:adjustedComponents];
+
+    if (didAdjust)
+        [self _updateSelectedPickerViewIndices];
+}
+
+- (RetainPtr<UILabel>)_createAndConfigureGranularityLabelWithText:(NSString *)localizedText
+{
+    auto label = adoptNS([[UILabel alloc] init]);
+    [label setText:localizedText];
+    [label setTextAlignment:NSTextAlignmentCenter];
+    [label setFont:[UIFont systemFontOfSize:pickerViewGranularityLabelFontSize weight:UIFontWeightHeavy]];
+    [label layer].backgroundColor = [UIColor systemGreenColor].CGColor;
+    [label _setContinuousCornerRadius:pickerViewGranularityLabelBorderRadius];
+    [label setTextColor:[UIColor blackColor]];
+    [label setHidden:YES];
+    [self.contentView addSubview:label.get()];
+    return label;
+}
+
+- (void)_canonicalizeAndUpdateSelectedDate
+{
+    auto eraAndYear = [self _eraAndYearFromIndex:[_yearAndEraPicker selectedIndex]];
+    [self setDay:[self _dayFromIndex:[_dayPicker selectedIndex]] month:[self _monthFromIndex:[_monthPicker selectedIndex]] year:eraAndYear.year era:eraAndYear.era];
+    [self _adjustDateToValidDateIfNecessary];
+    [_dayPicker reloadData];
+}
+
+#pragma mark - PUICPickerViewDataSource
+
+- (NSInteger)numberOfItemsInPickerView:(WKDatePickerWheel *)pickerView
+{
+    if (pickerView == _monthPicker)
+        return [_calendar rangeOfUnit:NSCalendarUnitMonth inUnit:NSCalendarUnitYear forDate:self.date].length;
+
+    if (pickerView == _dayPicker)
+        return [_calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date].length;
+
+    if (pickerView == _yearAndEraPicker) {
+        NSDateComponents *yearAndEraComponents = [_calendar components:NSCalendarUnitYear | NSCalendarUnitEra fromDate:self.maximumDate];
+        return 1 + [self _indexFromYear:yearAndEraComponents.year era:yearAndEraComponents.era];
+    }
+
+    return 0;
+}
+
+- (UIView *)pickerView:(WKDatePickerWheel *)pickerView viewForItemAtIndex:(NSInteger)index
+{
+    auto label = retainPtr((WKDatePickerWheelLabel *)[pickerView dequeueReusableItemView]) ?: adoptNS([[WKDatePickerWheelLabel alloc] init]);
+    if (![label needsUpdateForIndex:index selectedDate:self.date])
+        return label.autorelease();
+
+    if (pickerView == _monthPicker) {
+        NSDateComponents *dateComponentsAtIndex = [self _dateComponentForDay:NSNotFound month:[self _monthFromIndex:index] year:_year era:_era];
+        [label setText:[_standaloneMonthFormatter stringFromDate:dateComponentsAtIndex.date].localizedUppercaseString];
+    }
+
+    if (pickerView == _dayPicker) {
+        NSDateComponents *dateComponentsAtIndex = [self _dateComponentForDay:[self _dayFromIndex:index] month:_month year:_year era:_era];
+        [label setText:[_standaloneDayFormatter stringFromDate:dateComponentsAtIndex.date]];
+    }
+
+    if (pickerView == _yearAndEraPicker) {
+        auto eraAndYear = [self _eraAndYearFromIndex:index];
+        NSDate *dateForEraAndYear = [self _dateComponentForDay:NSNotFound month:NSNotFound year:eraAndYear.year era:eraAndYear.era].date;
+        NSString *eraPrefix = _displayCombinedEraAndYear ? [[[_calendar eraSymbols] objectAtIndex:eraAndYear.era] substringToIndex:1] : @"";
+        [label setText:[eraPrefix stringByAppendingString:[_standaloneYearFormatter stringFromDate:dateForEraAndYear]]];
+    }
+
+    [label setIndex:index];
+    [label setLastSelectedDate:self.date];
+    return label.autorelease();
+}
+
+- (void)didBeginInteractingWithDatePicker:(WKDatePickerWheel *)datePicker
+{
+    if (datePicker == _focusedPicker)
+        return;
+
+    [_monthPicker setDrawsFocusOutline:datePicker == _monthPicker];
+    [_monthLabel setHidden:![_monthPicker drawsFocusOutline]];
+    [_dayPicker setDrawsFocusOutline:datePicker == _dayPicker];
+    [_dayLabel setHidden:![_dayPicker drawsFocusOutline]];
+    [_yearAndEraPicker setDrawsFocusOutline:datePicker == _yearAndEraPicker];
+    [_yearLabel setHidden:![_yearAndEraPicker drawsFocusOutline]];
+
+    _focusedPicker = datePicker;
+    [self becomeFirstResponder];
+}
+
+- (void)pickerView:(WKDatePickerWheel *)pickerView didSelectItemAtIndex:(NSInteger)index
+{
+}
+
+- (void)pickerViewWillBeginSelection:(WKDatePickerWheel *)pickerView
+{
+    if (pickerView == _monthPicker)
+        [_monthPicker setChangingValue:YES];
+    else if (pickerView == _yearAndEraPicker)
+        [_yearAndEraPicker setChangingValue:YES];
+    else if (pickerView == _dayPicker)
+        [_dayPicker setChangingValue:YES];
+}
+
+- (void)pickerViewDidEndSelection:(WKDatePickerWheel *)pickerView
+{
+    if (pickerView == _monthPicker)
+        [_monthPicker setChangingValue:NO];
+    else if (pickerView == _yearAndEraPicker)
+        [_yearAndEraPicker setChangingValue:NO];
+    else if (pickerView == _dayPicker)
+        [_dayPicker setChangingValue:NO];
+    else if (![_monthPicker isChangingValue] && ![_yearAndEraPicker isChangingValue] && ![_dayPicker isChangingValue])
+        [self _canonicalizeAndUpdateSelectedDate];
+}
+
+#pragma mark - Conversion between picker view indices and era, year, month, day
+
+- (NSInteger)_dayFromIndex:(NSUInteger)dayIndex
+{
+    return dayIndex + 1;
+}
+
+- (EraAndYear)_eraAndYearFromIndex:(NSUInteger)yearIndex
+{
+    NSDate *dateAtYearIndex = [_calendar dateByAddingUnit:NSCalendarUnitYear value:yearIndex toDate:self.minimumDate options:0];
+    NSDateComponents *components = [_calendar components:NSCalendarUnitYear | NSCalendarUnitEra fromDate:dateAtYearIndex];
+    return { [components era], [components year] };
+}
+
+- (NSInteger)_monthFromIndex:(NSUInteger)monthIndex
+{
+    return monthIndex + 1;
+}
+
+- (NSUInteger)_indexFromDay:(NSInteger)day
+{
+    return day - 1;
+}
+
+- (NSUInteger)_indexFromYear:(NSInteger)year era:(NSInteger)era
+{
+    NSDate *dateForEraAndYear = [self _dateComponentForDay:NSNotFound month:NSNotFound year:year era:era].date;
+    NSDateComponents *components = [_calendar components:NSCalendarUnitYear fromDate:self.minimumDate toDate:dateForEraAndYear options:0];
+    return components.year;
+}
+
+- (NSUInteger)_indexFromMonth:(NSInteger)month
+{
+    return month - 1;
+}
+
+@end
+
+
 #endif
diff --git a/Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.h b/Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.h
deleted file mode 100644 (file)
index 1e21c89..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKFormControlListViewControllerAdditions.h>
-#endif
diff --git a/Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.mm b/Source/WebKit/UIProcess/ios/forms/WKFormControlListViewController.mm
deleted file mode 100644 (file)
index b7ba507..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-#include "config.h"
-#include "WKFormControlListViewController.h"
-
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKFormControlListViewControllerAdditions.mm>
-#endif
index 0ff01e5..fec98ac 100644 (file)
 
 #pragma once
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKNumberPadViewControllerAdditions.h>
+#if PLATFORM(WATCHOS)
+
+#import "WKNumberPadView.h"
+#import "WKTextInputListViewController.h"
+#import <PepperUICore/PUICQuickboardViewController.h>
+
+@interface WKNumberPadViewController : PUICQuickboardViewController
+
+- (instancetype)initWithDelegate:(id <PUICQuickboardViewControllerDelegate>)delegate initialText:(NSString *)initialText inputMode:(WKNumberPadInputMode)inputMode;
+- (void)didSelectKey:(WKNumberPadKey)key;
+
+@property (nonatomic, readonly) WKNumberPadInputMode inputMode;
+
+@end
+
 #endif
index 4ee85c0..ece0c44 100644 (file)
 #include "config.h"
 #include "WKNumberPadViewController.h"
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKNumberPadViewControllerAdditions.mm>
-#endif
+#if PLATFORM(WATCHOS)
+
+#import "UIKitSPI.h"
+#import "WKNumberPadView.h"
+#import <PepperUICore/PUICQuickboardViewController_Private.h>
+#import <PepperUICore/PUICResources.h>
+#import <PepperUICore/UIDevice+PUICAdditions.h>
+#import <WebCore/LocalizedStrings.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/WeakObjCPtr.h>
+#import <wtf/text/WTFString.h>
+
+static const CGFloat numberPadViewTopMargin = 30;
+static const CGFloat headerButtonWidth = 24;
+static const CGFloat inputLabelMinimumScale = 0.7;
+static const CGFloat numberPadViewDismissAnimationDuration = 0.3;
+static const NSTimeInterval numberPadDeleteKeyRepeatDelay = 0.35;
+static const NSTimeInterval numberPadDeleteKeyRepeatInterval = 0.1;
+static CGFloat inputLabelFontSize()
+{
+    if ([[UIDevice currentDevice] puic_deviceVariant] == PUICDeviceVariantCompact)
+        return 16;
+    return 18;
+}
+
+@implementation WKNumberPadViewController {
+    RetainPtr<NSMutableString> _inputText;
+    RetainPtr<WKNumberPadView> _numberPadView;
+    WKNumberPadInputMode _inputMode;
+    RetainPtr<UILabel> _inputLabel;
+    RetainPtr<UIButton> _deleteButton;
+    RetainPtr<UIButton> _backChevronButton;
+    BOOL _shouldDismissWithFadeAnimation;
+}
+
+- (instancetype)initWithDelegate:(id <PUICQuickboardViewControllerDelegate>)delegate initialText:(NSString *)initialText inputMode:(WKNumberPadInputMode)inputMode
+{
+    if (!(self = [super initWithDelegate:delegate]))
+        return nil;
+
+    _inputText = adoptNS(initialText.mutableCopy);
+    _inputMode = inputMode;
+    _shouldDismissWithFadeAnimation = NO;
+    return self;
+}
+
+- (void)dealloc
+{
+    [NSObject cancelPreviousPerformRequestsWithTarget:self];
+    [super dealloc];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+
+    _numberPadView = adoptNS([[WKNumberPadView alloc] initWithFrame:UIRectInset(self.contentView.bounds, numberPadViewTopMargin, 0, 0, 0) controller:self]);
+    [self.contentView addSubview:_numberPadView.get()];
+
+    _inputLabel = adoptNS([[UILabel alloc] init]);
+    [_inputLabel setFont:[UIFont systemFontOfSize:inputLabelFontSize() weight:UIFontWeightSemibold]];
+    [_inputLabel setTextColor:[UIColor whiteColor]];
+    [_inputLabel setLineBreakMode:NSLineBreakByTruncatingHead];
+    [_inputLabel setTextAlignment:NSTextAlignmentCenter];
+    [_inputLabel setMinimumScaleFactor:inputLabelMinimumScale];
+    [_inputLabel setAdjustsFontSizeToFitWidth:YES];
+    [self.headerView addSubview:_inputLabel.get()];
+
+    _deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    UIImage *deleteButtonIcon = [[PUICResources imageNamed:@"keypad-delete-glyph" inBundle:[NSBundle bundleWithIdentifier:@"com.apple.PepperUICore"] shouldCache:YES] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    [_deleteButton setImage:deleteButtonIcon forState:UIControlStateNormal];
+    [_deleteButton setTintColor:[UIColor systemRedColor]];
+    [_deleteButton addTarget:self action:@selector(_startDeletionTimer) forControlEvents:UIControlEventTouchDown];
+    [_deleteButton addTarget:self action:@selector(_deleteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
+    [_deleteButton addTarget:self action:@selector(_cancelDeletionTimers) forControlEvents:UIControlEventTouchUpOutside];
+    [self.headerView addSubview:_deleteButton.get()];
+
+    _backChevronButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    UIImage *backChevronButtonIcon = [[PUICResources imageNamed:@"status-bar-chevron" inBundle:[NSBundle bundleWithIdentifier:@"com.apple.PepperUICore"] shouldCache:YES] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    [_backChevronButton setImage:backChevronButtonIcon forState:UIControlStateNormal];
+    [_backChevronButton setTintColor:[UIColor systemGrayColor]];
+    [_backChevronButton addTarget:self action:@selector(_cancelInput) forControlEvents:UIControlEventTouchUpInside];
+    [self.headerView addSubview:_backChevronButton.get()];
+
+    [self _reloadHeaderViewFromInputText];
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    [super viewWillDisappear:animated];
+    [self _cancelDeletionTimers];
+}
+
+- (void)viewWillLayoutSubviews
+{
+    [super viewWillLayoutSubviews];
+
+    [_inputLabel setFrame:UIRectInsetEdges(self.headerView.bounds, UIRectEdgeLeft | UIRectEdgeRight, headerButtonWidth)];
+    [_deleteButton setFrame:CGRectMake(CGRectGetWidth(self.headerView.bounds) - headerButtonWidth, 0, headerButtonWidth, CGRectGetHeight(self.headerView.bounds))];
+    [_backChevronButton setFrame:CGRectMake(0, 0, headerButtonWidth, CGRectGetHeight(self.headerView.bounds))];
+}
+
+- (void)_reloadHeaderViewFromInputText
+{
+    BOOL hasInputText = [_inputText length];
+    self.cancelButton.hidden = hasInputText;
+    [_deleteButton setHidden:!hasInputText];
+    [_backChevronButton setHidden:!hasInputText];
+    [_inputLabel setText:_inputText.get()];
+}
+
+- (void)didSelectKey:(WKNumberPadKey)key
+{
+    [self _handleKeyPress:key];
+}
+
+- (void)_handleKeyPress:(WKNumberPadKey)key
+{
+    switch (key) {
+    case WKNumberPadKeyDash:
+        [_inputText appendString:@"-"];
+        break;
+    case WKNumberPadKeyAsterisk:
+        [_inputText appendString:@"*"];
+        break;
+    case WKNumberPadKeyOctothorpe:
+        [_inputText appendString:@"#"];
+        break;
+    case WKNumberPadKeyClosingParenthesis:
+        [_inputText appendString:@")"];
+        break;
+    case WKNumberPadKeyOpeningParenthesis:
+        [_inputText appendString:@"("];
+        break;
+    case WKNumberPadKeyPlus:
+        [_inputText appendString:@"+"];
+        break;
+    case WKNumberPadKeyAccept:
+        [self.delegate quickboard:self textEntered:[[[NSAttributedString alloc] initWithString:_inputText.get()] autorelease]];
+        return;
+    case WKNumberPadKey0:
+        [_inputText appendString:@"0"];
+        break;
+    case WKNumberPadKey1:
+        [_inputText appendString:@"1"];
+        break;
+    case WKNumberPadKey2:
+        [_inputText appendString:@"2"];
+        break;
+    case WKNumberPadKey3:
+        [_inputText appendString:@"3"];
+        break;
+    case WKNumberPadKey4:
+        [_inputText appendString:@"4"];
+        break;
+    case WKNumberPadKey5:
+        [_inputText appendString:@"5"];
+        break;
+    case WKNumberPadKey6:
+        [_inputText appendString:@"6"];
+        break;
+    case WKNumberPadKey7:
+        [_inputText appendString:@"7"];
+        break;
+    case WKNumberPadKey8:
+        [_inputText appendString:@"8"];
+        break;
+    case WKNumberPadKey9:
+        [_inputText appendString:@"9"];
+        break;
+    default:
+        break;
+    }
+
+    [self _cancelDeletionTimers];
+    [self _reloadHeaderViewFromInputText];
+}
+
+- (void)_cancelInput
+{
+    _shouldDismissWithFadeAnimation = YES;
+    [self.delegate quickboardInputCancelled:self];
+}
+
+- (void)_deleteLastInputCharacter
+{
+    if (![_inputText length])
+        return;
+
+    [_inputText deleteCharactersInRange:NSMakeRange([_inputText length] - 1, 1)];
+    [self _reloadHeaderViewFromInputText];
+}
+
+- (void)_deleteButtonPressed
+{
+    [self _deleteLastInputCharacter];
+    [self _cancelDeletionTimers];
+}
+
+- (void)_cancelDeletionTimers
+{
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_startDeletionTimer) object:nil];
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_deletionTimerFired) object:nil];
+}
+
+- (void)_startDeletionTimer
+{
+    [self _cancelDeletionTimers];
+    [self performSelector:@selector(_deletionTimerFired) withObject:nil afterDelay:numberPadDeleteKeyRepeatDelay];
+}
+
+- (void)_deletionTimerFired
+{
+    [self _cancelDeletionTimers];
+    [self _deleteLastInputCharacter];
+    if ([_inputText length])
+        [self performSelector:@selector(_deletionTimerFired) withObject:nil afterDelay:numberPadDeleteKeyRepeatInterval];
+}
+
+#pragma mark - PUICQuickboardViewController overrides
+
+- (void)addContentViewAnimations:(BOOL)isPresenting
+{
+    if (!_shouldDismissWithFadeAnimation) {
+        [super addContentViewAnimations:isPresenting];
+        return;
+    }
+
+    CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+    fadeOutAnimation.fromValue = @1;
+    fadeOutAnimation.toValue = @0;
+    fadeOutAnimation.duration = numberPadViewDismissAnimationDuration;
+    fadeOutAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    [self.contentView addAnimation:fadeOutAnimation forKey:@"WebKitNumberPadFadeOutAnimationKey"];
+    self.contentView.alpha = 0;
+}
+
+@end
+
+#endif // PLATFORM(WATCHOS)
diff --git a/Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.h b/Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.h
new file mode 100644 (file)
index 0000000..c6c4c04
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#if PLATFORM(WATCHOS)
+
+#import "UIKitSPI.h"
+#import <PepperUICore/PUICQuickboardListViewController.h>
+#import <PepperUICore/PUICQuickboardListViewControllerSubclass.h>
+#import <PepperUICore/PUICTableViewCell.h>
+
+@interface WKQuickboardListItemCell : PUICQuickboardListItemCell
+@end
+
+@class WKQuickboardListViewController;
+
+@protocol WKQuickboardViewControllerDelegate <PUICQuickboardViewControllerDelegate>
+
+- (CGFloat)viewController:(PUICQuickboardViewController *)controller inputContextViewHeightForSize:(CGSize)size;
+- (UIView *)inputContextViewForViewController:(PUICQuickboardViewController *)controller;
+- (NSString *)inputLabelTextForViewController:(PUICQuickboardViewController *)controller;
+- (NSString *)initialValueForViewController:(PUICQuickboardViewController *)controller;
+- (BOOL)shouldDisplayInputContextViewForListViewController:(PUICQuickboardViewController *)controller;
+- (BOOL)allowsLanguageSelectionMenuForListViewController:(PUICQuickboardViewController *)controller;
+
+@end
+
+@interface WKQuickboardListViewController : PUICQuickboardListViewController
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
+- (instancetype)initWithDelegate:(id<PUICQuickboardViewControllerDelegate>)delegate dictationMode:(PUICDictationMode)dictationMode NS_UNAVAILABLE;
+
+- (void)reloadContextView;
+
+@property (nonatomic, weak) id <WKQuickboardViewControllerDelegate> delegate;
+
+@end
+
+void configureStatusBarForController(PUICQuickboardViewController *, id <WKQuickboardViewControllerDelegate>);
+
+#endif
diff --git a/Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.mm b/Source/WebKit/UIProcess/ios/forms/WKQuickboardListViewController.mm
new file mode 100644 (file)
index 0000000..ca05759
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "WKQuickboardListViewController.h"
+
+#if PLATFORM(WATCHOS)
+
+#import <PepperUICore/PUICActionController.h>
+#import <PepperUICore/PUICActionGroup.h>
+#import <PepperUICore/PUICApplication_Private.h>
+#import <PepperUICore/PUICQuickboardLanguageController.h>
+#import <PepperUICore/PUICQuickboardViewController_Private.h>
+#import <PepperUICore/PUICStatusBarAppContextView.h>
+#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
+
+@interface WKQuickboardListViewController () <PUICQuickboardLanguageControllerDelegate>
+@end
+
+@implementation WKQuickboardListViewController {
+    BOOL _contextViewNeedsUpdate;
+    RetainPtr<UIView> _contextView;
+    RetainPtr<PUICStatusBarGlobalContextViewAssertion> _statusBarAssertion;
+}
+
+@dynamic delegate;
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate
+{
+    if (self = [super initWithDelegate:delegate dictationMode:PUICDictationModeText])
+        _contextViewNeedsUpdate = YES;
+
+    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];
+}
+
+- (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];
+}
+
+#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)
index b67b18a..2122451 100644 (file)
 
 #pragma once
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKSelectMenuListViewControllerAdditions.h>
+#if PLATFORM(WATCHOS)
+
+#import "WKQuickboardListViewController.h"
+
+@class WKSelectMenuListViewController;
+
+@protocol WKSelectMenuListViewControllerDelegate <WKQuickboardViewControllerDelegate>
+
+- (void)selectMenu:(WKSelectMenuListViewController *)selectMenu didSelectItemAtIndex:(NSUInteger)index;
+- (void)selectMenu:(WKSelectMenuListViewController *)selectMenu didCheckItemAtIndex:(NSUInteger)index checked:(BOOL)checked;
+
+- (BOOL)selectMenu:(WKSelectMenuListViewController *)selectMenu hasSelectedOptionAtIndex:(NSUInteger)index;
+- (NSUInteger)numberOfItemsInSelectMenu:(WKSelectMenuListViewController *)selectMenu;
+- (NSString *)selectMenu:(WKSelectMenuListViewController *)selectMenu displayTextForItemAtIndex:(NSUInteger)index;
+- (BOOL)selectMenuUsesMultipleSelection:(WKSelectMenuListViewController *)selectMenu;
+
+@end
+
+@interface WKSelectMenuListViewController : WKQuickboardListViewController
+
+- (instancetype)initWithDelegate:(id <WKSelectMenuListViewControllerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
+
+@property (nonatomic, weak) id <WKSelectMenuListViewControllerDelegate> delegate;
+
+@end
+
+@interface WKSelectMenuListViewController (Testing)
+
+- (void)selectItemAtIndex:(NSInteger)index;
+
+@end
+
 #endif
index 362148d..05b2be7 100644 (file)
 #import "config.h"
 #import "WKSelectMenuListViewController.h"
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/WKSelectMenuListViewControllerAdditions.mm>
-#endif
+#if PLATFORM(WATCHOS)
+
+#import "UIKitSPI.h"
+#import <PepperUICore/PUICQuickboardListViewController.h>
+#import <PepperUICore/PUICQuickboardListViewControllerSubclass.h>
+#import <PepperUICore/PUICResources.h>
+#import <PepperUICore/PUICTableView.h>
+#import <PepperUICore/PUICTableViewCell.h>
+#import <wtf/RetainPtr.h>
+
+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,
+};
+
+@interface WKSelectMenuItemCell : WKQuickboardListItemCell
+@property (nonatomic, readonly) UIImageView *imageView;
+@end
+
+@implementation WKSelectMenuItemCell {
+    RetainPtr<UIImageView> _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();
+}
+
+@end
+
+@implementation WKSelectMenuListViewController {
+    BOOL _isMultipleSelect;
+    RetainPtr<NSMutableIndexSet> _indicesOfCheckedOptions;
+}
+
+@dynamic delegate;
+
+- (instancetype)initWithDelegate:(id <WKSelectMenuListViewControllerDelegate>)delegate
+{
+    return [super initWithDelegate:delegate];
+}
+
+- (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;
+}
+
+- (void)didSelectListItem:(NSInteger)itemNumber
+{
+    NSMutableArray *indexPathsToReload = [NSMutableArray array];
+    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:PUICQuickboardListSectionTextOptions]];
+    } 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:PUICQuickboardListSectionTextOptions]];
+            [indexPathsToReload addObject:[NSIndexPath indexPathForRow:itemNumber inSection:PUICQuickboardListSectionTextOptions]];
+        }
+    }
+
+    if (indexPathsToReload.count)
+        [self.listView reloadRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationNone];
+}
+
+- (NSInteger)numberOfListItems
+{
+    return [self.delegate numberOfItemsInSelectMenu:self];
+}
+
+- (CGFloat)heightForListItem:(NSInteger)itemNumber width:(CGFloat)width
+{
+    return selectMenuItemCellHeight;
+}
+
+- (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();
+}
+
+- (void)selectItemAtIndex:(NSInteger)index
+{
+    [self didSelectListItem:index];
+}
+
+@end
+
+#endif // PLATFORM(WATCHOS)
index 7128e44..1c159d2 100644 (file)
 
 #pragma once
 
-#if USE(APPLE_INTERNAL_SDK) && __has_include(<WebKitAdditions/WKTimePickerViewControllerAdditions.h>)
-#import <WebKitAdditions/WKTimePickerViewControllerAdditions.h>
+#if PLATFORM(WATCHOS)
+
+#import "WKQuickboardListViewController.h"
+
+@interface WKTimePickerViewController : PUICQuickboardViewController
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
+
+@property (nonatomic, weak) id <WKQuickboardViewControllerDelegate> delegate;
+
+@end
+
+@interface WKTimePickerViewController (Testing)
+
+- (void)setHour:(NSInteger)hour minute:(NSInteger)minute;
+
+@end
+
 #endif
index a5b1611..9e6902c 100644 (file)
 #include "config.h"
 #include "WKTimePickerViewController.h"
 
-#if USE(APPLE_INTERNAL_SDK) && __has_include(<WebKitAdditions/WKTimePickerViewControllerAdditions.mm>)
-#import <WebKitAdditions/WKTimePickerViewControllerAdditions.mm>
-#endif
+#if PLATFORM(WATCHOS)
+
+#import <ClockKitUI/CLKUIWheelsOfTimeView.h>
+#import <ClockKitUI/ClockKitUI.h>
+#import <PepperUICore/PUICQuickboardViewController.h>
+#import <PepperUICore/PUICQuickboardViewController_Private.h>
+#import <UIKit/UIResponder_Private.h>
+#import <WebCore/LocalizedStrings.h>
+#import <wtf/SoftLinking.h>
+#import <wtf/text/WTFString.h>
+
+SOFT_LINK_PRIVATE_FRAMEWORK(ClockKitUI)
+SOFT_LINK_CLASS(ClockKitUI, CLKUIWheelsOfTimeView)
+
+static NSString *timePickerTimeZoneForFormatting = @"UTC";
+static NSString *timePickerDateFormat = @"HH:mm";
+
+@interface WKTimePickerViewController () <CLKUIWheelsOfTimeDelegate>
+@end
+
+@implementation WKTimePickerViewController {
+    RetainPtr<CLKUIWheelsOfTimeView> _timePicker;
+    RetainPtr<NSDateFormatter> _dateFormatter;
+}
+
+@dynamic delegate;
+
+- (instancetype)initWithDelegate:(id <WKQuickboardViewControllerDelegate>)delegate
+{
+    return [super initWithDelegate:delegate];
+}
+
+- (NSDateFormatter *)dateFormatter
+{
+    if (_dateFormatter)
+        return _dateFormatter.get();
+
+    _dateFormatter = adoptNS([[NSDateFormatter alloc] init]);
+    [_dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+    [_dateFormatter setDateFormat:timePickerDateFormat];
+    [_dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:timePickerTimeZoneForFormatting]];
+    return _dateFormatter.get();
+}
+
+- (NSString *)timeValueForFormControls
+{
+    NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
+    calendar.timeZone = [NSTimeZone timeZoneWithName:timePickerTimeZoneForFormatting];
+
+    NSDate *epochDate = [NSDate dateWithTimeIntervalSince1970:0];
+    NSDate *timePickerDateAsOffsetFromEpoch = [calendar dateBySettingHour:[_timePicker hour] minute:[_timePicker minute] second:0 ofDate:epochDate options:0];
+    return [self.dateFormatter stringFromDate:timePickerDateAsOffsetFromEpoch];
+}
+
+- (NSDateComponents *)dateComponentsFromInitialValue
+{
+    NSString *initialText = [self.delegate initialValueForViewController:self];
+    if (initialText.length < timePickerDateFormat.length)
+        return nil;
+
+    NSString *truncatedInitialValue = [initialText substringToIndex:timePickerDateFormat.length];
+    NSDate *parsedDate = [self.dateFormatter dateFromString:truncatedInitialValue];
+    if (!parsedDate)
+        return nil;
+
+    NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
+    calendar.timeZone = [NSTimeZone timeZoneWithName:timePickerTimeZoneForFormatting];
+    return [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute fromDate:parsedDate];
+}
+
+#pragma mark - UIViewController overrides
+
+- (void)viewDidAppear:(BOOL)animated
+{
+    [super viewDidAppear:animated];
+    [self becomeFirstResponder];
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+
+    self.headerView.hidden = YES;
+
+    _timePicker = adoptNS([allocCLKUIWheelsOfTimeViewInstance() initWithFrame:self.view.bounds style:CLKUIWheelsOfTimeStyleAlarm12]);
+    [_timePicker setDelegate:self];
+
+    NSDateComponents *components = self.dateComponentsFromInitialValue;
+    if (components)
+        [_timePicker setHour:components.hour andMinute:components.minute];
+
+    [self.contentView addSubview:_timePicker.get()];
+}
+
+- (BOOL)becomeFirstResponder
+{
+    return [_timePicker becomeFirstResponder];
+}
+
+- (void)setHour:(NSInteger)hour minute:(NSInteger)minute
+{
+    [_timePicker setHour:hour andMinute:minute];
+    [self rightButtonWOTAction];
+}
+
+#pragma mark - CLKUIWheelsOfTimeDelegate
+
+- (void)leftButtonWOTAction
+{
+    // Handle an action on the 'Cancel' button.
+    [self.delegate quickboardInputCancelled:self];
+}
+
+- (void)rightButtonWOTAction
+{
+    // Handle an action on the 'Set' button.
+    auto valueAsAttributedString = adoptNS([[NSAttributedString alloc] initWithString:self.timeValueForFormControls]);
+    [self.delegate quickboard:self textEntered:valueAsAttributedString.get()];
+}
+
+@end
+
+#endif // PLATFORM(WATCHOS)
index d8d2951..6a50721 100644 (file)
                F496A4321F58A272004C1757 /* DragDropInteractionState.mm in Sources */ = {isa = PBXBuildFile; fileRef = F496A4301F58A272004C1757 /* DragDropInteractionState.mm */; };
                F4D5F51D206087A10038BBA8 /* WKTextInputListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D5F519206087A00038BBA8 /* WKTextInputListViewController.h */; };
                F4D5F51E206087A10038BBA8 /* WKTextInputListViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D5F51A206087A10038BBA8 /* WKTextInputListViewController.mm */; };
-               F4D5F51F206087A10038BBA8 /* WKFormControlListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D5F51B206087A10038BBA8 /* WKFormControlListViewController.h */; };
-               F4D5F520206087A10038BBA8 /* WKFormControlListViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D5F51C206087A10038BBA8 /* WKFormControlListViewController.mm */; };
+               F4D5F51F206087A10038BBA8 /* WKQuickboardListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D5F51B206087A10038BBA8 /* WKQuickboardListViewController.h */; };
+               F4D5F520206087A10038BBA8 /* WKQuickboardListViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D5F51C206087A10038BBA8 /* WKQuickboardListViewController.mm */; };
                F4F59AD62065AEB1006CAA46 /* WKSelectMenuListViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4F59AD32065A5C9006CAA46 /* WKSelectMenuListViewController.mm */; };
                F6113E25126CE1820057D0A7 /* APIUserContentURLPattern.h in Headers */ = {isa = PBXBuildFile; fileRef = F6113E24126CE1820057D0A7 /* APIUserContentURLPattern.h */; };
                F6113E28126CE19B0057D0A7 /* WKUserContentURLPattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6113E26126CE19B0057D0A7 /* WKUserContentURLPattern.cpp */; };
                F496A4301F58A272004C1757 /* DragDropInteractionState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = DragDropInteractionState.mm; path = ios/DragDropInteractionState.mm; sourceTree = "<group>"; };
                F4D5F519206087A00038BBA8 /* WKTextInputListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKTextInputListViewController.h; path = ios/forms/WKTextInputListViewController.h; sourceTree = "<group>"; };
                F4D5F51A206087A10038BBA8 /* WKTextInputListViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKTextInputListViewController.mm; path = ios/forms/WKTextInputListViewController.mm; sourceTree = "<group>"; };
-               F4D5F51B206087A10038BBA8 /* WKFormControlListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKFormControlListViewController.h; path = ios/forms/WKFormControlListViewController.h; sourceTree = "<group>"; };
-               F4D5F51C206087A10038BBA8 /* WKFormControlListViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKFormControlListViewController.mm; path = ios/forms/WKFormControlListViewController.mm; sourceTree = "<group>"; };
+               F4D5F51B206087A10038BBA8 /* WKQuickboardListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKQuickboardListViewController.h; path = ios/forms/WKQuickboardListViewController.h; sourceTree = "<group>"; };
+               F4D5F51C206087A10038BBA8 /* WKQuickboardListViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKQuickboardListViewController.mm; path = ios/forms/WKQuickboardListViewController.mm; sourceTree = "<group>"; };
                F4F59AD32065A5C9006CAA46 /* WKSelectMenuListViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKSelectMenuListViewController.mm; path = ios/forms/WKSelectMenuListViewController.mm; sourceTree = "<group>"; };
                F4F59AD42065A5CA006CAA46 /* WKSelectMenuListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKSelectMenuListViewController.h; path = ios/forms/WKSelectMenuListViewController.h; sourceTree = "<group>"; };
                F6113E24126CE1820057D0A7 /* APIUserContentURLPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIUserContentURLPattern.h; sourceTree = "<group>"; };
                                A58B6F0718FCA733008CBA53 /* WKFileUploadPanel.mm */,
                                2E16B6CD2017B7AC008996D6 /* WKFocusedFormControlView.h */,
                                2E16B6CC2017B7AB008996D6 /* WKFocusedFormControlView.mm */,
-                               F4D5F51B206087A10038BBA8 /* WKFormControlListViewController.h */,
-                               F4D5F51C206087A10038BBA8 /* WKFormControlListViewController.mm */,
                                C54256AF18BEC18B00DE4179 /* WKFormInputControl.h */,
                                C54256B018BEC18B00DE4179 /* WKFormInputControl.mm */,
                                C54256B118BEC18B00DE4179 /* WKFormPeripheral.h */,
                                2EA7B3D42026CF23009CE5AC /* WKNumberPadView.mm */,
                                2EA7B3CF2026CEF8009CE5AC /* WKNumberPadViewController.h */,
                                2EA7B3D02026CEF8009CE5AC /* WKNumberPadViewController.mm */,
+                               F4D5F51B206087A10038BBA8 /* WKQuickboardListViewController.h */,
+                               F4D5F51C206087A10038BBA8 /* WKQuickboardListViewController.mm */,
                                F4F59AD42065A5CA006CAA46 /* WKSelectMenuListViewController.h */,
                                F4F59AD32065A5C9006CAA46 /* WKSelectMenuListViewController.mm */,
                                F4D5F519206087A00038BBA8 /* WKTextInputListViewController.h */,
                                A58B6F0818FCA733008CBA53 /* WKFileUploadPanel.h in Headers */,
                                37F623B812A57B6200E3FDF6 /* WKFindOptions.h in Headers */,
                                2E16B6CF2017B7AD008996D6 /* WKFocusedFormControlView.h in Headers */,
-                               F4D5F51F206087A10038BBA8 /* WKFormControlListViewController.h in Headers */,
                                C54256B518BEC18C00DE4179 /* WKFormInputControl.h in Headers */,
                                C54256B718BEC18C00DE4179 /* WKFormPeripheral.h in Headers */,
                                C54256B818BEC18C00DE4179 /* WKFormPopover.h in Headers */,
                                512F58FC12A88A5400629530 /* WKProtectionSpace.h in Headers */,
                                5272D4C91E735F0900EB4290 /* WKProtectionSpaceNS.h in Headers */,
                                518ACAEA12AEE6BB00B04B83 /* WKProtectionSpaceTypes.h in Headers */,
+                               F4D5F51F206087A10038BBA8 /* WKQuickboardListViewController.h in Headers */,
                                1AD01BCD1905D54900C9C45F /* WKReloadFrameErrorRecoveryAttempter.h in Headers */,
                                1A9E329B1822E1CC00F5D04C /* WKRemoteObject.h in Headers */,
                                1A9E329F1822FEDD00F5D04C /* WKRemoteObjectCoder.h in Headers */,
                                BC4075FB124FF0270068F20A /* WKErrorRef.cpp in Sources */,
                                A58B6F0918FCA733008CBA53 /* WKFileUploadPanel.mm in Sources */,
                                2E16B6CE2017B7AD008996D6 /* WKFocusedFormControlView.mm in Sources */,
-                               F4D5F520206087A10038BBA8 /* WKFormControlListViewController.mm in Sources */,
                                C54256B618BEC18C00DE4179 /* WKFormInputControl.mm in Sources */,
                                C54256B918BEC18C00DE4179 /* WKFormPopover.mm in Sources */,
                                C57193BE18C14A44002D0F12 /* WKFormSelectControl.mm in Sources */,
                                1A158419189044F50017616C /* WKProcessPool.mm in Sources */,
                                512F58FB12A88A5400629530 /* WKProtectionSpace.cpp in Sources */,
                                5272D4CA1E735F0900EB4290 /* WKProtectionSpaceNS.mm in Sources */,
+                               F4D5F520206087A10038BBA8 /* WKQuickboardListViewController.mm in Sources */,
                                1AD01BCC1905D54900C9C45F /* WKReloadFrameErrorRecoveryAttempter.mm in Sources */,
                                1A9E329A1822E1CC00F5D04C /* WKRemoteObject.mm in Sources */,
                                1A9E329E1822FEDD00F5D04C /* WKRemoteObjectCoder.mm in Sources */,