[go: nahoru, domu]

blob: 16d63926a89972f10d14480db327fc9bef31f722 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h"
#import "base/apple/foundation_util.h"
#import "base/ios/ios_util.h"
#import "ios/chrome/browser/search_engines/search_engine_observer_bridge.h"
#import "ios/chrome/browser/search_engines/search_engines_util.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/public/features/system_flags.h"
#import "ios/chrome/browser/shared/ui/elements/extended_touch_target_button.h"
#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
#import "ios/chrome/browser/ui/lens/lens_availability.h"
#import "ios/chrome/browser/ui/lens/lens_entrypoint.h"
#import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h"
#import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views_utils.h"
#import "ios/chrome/common/button_configuration_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#import "ios/public/provider/chrome/browser/lens/lens_api.h"
@interface OmniboxKeyboardAccessoryView () <SearchEngineObserving>
@property(nonatomic, retain) NSArray<NSString*>* buttonTitles;
@property(nonatomic, weak) id<OmniboxAssistiveKeyboardDelegate> delegate;
@property(nonatomic, weak) id<UIPasteConfigurationSupporting> pasteTarget;
// The shortcut stack view that is displayed by this view.
@property(nonatomic, weak) UIStackView* shortcutStackView;
// The search stack view that is displayed by this view.
@property(nonatomic, weak) UIStackView* searchStackView;
// The text field that this view is an accessory to.
@property(nonatomic, weak) UITextField* textField;
// Called when a keyboard shortcut button is pressed.
- (void)keyboardButtonPressed:(NSString*)title;
// Creates a button shortcut for `title`.
- (UIView*)shortcutButtonWithTitle:(NSString*)title;
@end
@implementation OmniboxKeyboardAccessoryView {
std::unique_ptr<SearchEngineObserverBridge> _searchEngineObserver;
}
@synthesize buttonTitles = _buttonTitles;
@synthesize delegate = _delegate;
- (instancetype)initWithButtons:(NSArray<NSString*>*)buttonTitles
delegate:(id<OmniboxAssistiveKeyboardDelegate>)delegate
pasteTarget:(id<UIPasteConfigurationSupporting>)pasteTarget
templateURLService:(TemplateURLService*)templateURLService
textField:(UITextField*)textField {
self = [super initWithFrame:CGRectZero
inputViewStyle:UIInputViewStyleKeyboard];
if (self) {
_buttonTitles = buttonTitles;
_delegate = delegate;
_pasteTarget = pasteTarget;
_textField = textField;
self.translatesAutoresizingMaskIntoConstraints = NO;
self.allowsSelfSizing = YES;
self.templateURLService = templateURLService;
[self addSubviews];
}
return self;
}
- (void)addSubviews {
if (!self.subviews.count)
return;
// Remove any existing stack views from this view.
[self.shortcutStackView removeFromSuperview];
[self.searchStackView removeFromSuperview];
const CGFloat kButtonMinWidth = 36.0;
const CGFloat kButtonHeight = 36.0;
const CGFloat kBetweenShortcutButtonSpacing = 5.0;
const CGFloat kBetweenSearchButtonSpacing = 12.0;
const CGFloat kHorizontalMargin = 10.0;
const CGFloat kVerticalMargin = 4.0;
// Create and add stackview filled with the shortcut buttons.
UIStackView* shortcutStackView = [[UIStackView alloc] init];
shortcutStackView.translatesAutoresizingMaskIntoConstraints = NO;
shortcutStackView.spacing = kBetweenShortcutButtonSpacing;
shortcutStackView.alignment = UIStackViewAlignmentCenter;
for (NSString* title in self.buttonTitles) {
UIView* button = [self shortcutButtonWithTitle:title];
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
[button.widthAnchor constraintGreaterThanOrEqualToConstant:kButtonMinWidth]
.active = YES;
[button.heightAnchor constraintEqualToConstant:kButtonHeight].active = YES;
[shortcutStackView addArrangedSubview:button];
}
[self addSubview:shortcutStackView];
self.shortcutStackView = shortcutStackView;
// Create and add a stackview containing the leading assistive buttons, i.e.
// Voice search, camera/Lens search and paste search.
BOOL useLens = ios::provider::IsLensSupported() &&
base::FeatureList::IsEnabled(kEnableLensInKeyboard) &&
[self isGoogleSearchEngine:self.templateURLService];
NSArray<UIControl*>* leadingControls =
OmniboxAssistiveKeyboardLeadingControls(_delegate, self.pasteTarget,
useLens);
UIStackView* searchStackView = [[UIStackView alloc] init];
searchStackView.translatesAutoresizingMaskIntoConstraints = NO;
searchStackView.spacing = kBetweenSearchButtonSpacing;
for (UIControl* button in leadingControls) {
[searchStackView addArrangedSubview:button];
}
[self addSubview:searchStackView];
self.searchStackView = searchStackView;
// Position the stack views.
id<LayoutGuideProvider> layoutGuide = self.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[searchStackView.leadingAnchor
constraintEqualToAnchor:layoutGuide.leadingAnchor
constant:kHorizontalMargin],
[shortcutStackView.trailingAnchor
constraintEqualToAnchor:layoutGuide.trailingAnchor
constant:-kHorizontalMargin],
[searchStackView.trailingAnchor
constraintLessThanOrEqualToAnchor:shortcutStackView.leadingAnchor],
[searchStackView.topAnchor constraintEqualToAnchor:layoutGuide.topAnchor
constant:kVerticalMargin],
[searchStackView.bottomAnchor
constraintEqualToAnchor:layoutGuide.bottomAnchor
constant:-kVerticalMargin],
[shortcutStackView.topAnchor
constraintEqualToAnchor:searchStackView.topAnchor],
[shortcutStackView.bottomAnchor
constraintEqualToAnchor:searchStackView.bottomAnchor],
]];
}
- (UIView*)shortcutButtonWithTitle:(NSString*)title {
const CGFloat kHorizontalEdgeInset = 8;
const CGFloat kButtonTitleFontSize = 16.0;
UIColor* kTitleColorStateNormal = [UIColor colorWithWhite:0.0 alpha:1.0];
UIColor* kTitleColorStateHighlighted = [UIColor colorWithWhite:0.0 alpha:0.3];
UIButton* button =
[ExtendedTouchTargetButton buttonWithType:UIButtonTypeCustom];
if (IsUIButtonConfigurationEnabled()) {
UIButtonConfiguration* buttonConfiguration =
[UIButtonConfiguration plainButtonConfiguration];
buttonConfiguration.contentInsets = NSDirectionalEdgeInsetsMake(
0, kHorizontalEdgeInset, 0, kHorizontalEdgeInset);
UIFont* font = [UIFont systemFontOfSize:kButtonTitleFontSize
weight:UIFontWeightMedium];
NSAttributedString* attributedTitle = [[NSAttributedString alloc]
initWithString:title
attributes:@{NSFontAttributeName : font}];
buttonConfiguration.attributedTitle = attributedTitle;
buttonConfiguration.baseForegroundColor =
[UIColor colorNamed:kTextPrimaryColor];
button.configuration = buttonConfiguration;
button.configurationUpdateHandler = ^(UIButton* incomingButton) {
UIButtonConfiguration* updatedConfig = incomingButton.configuration;
switch (incomingButton.state) {
case UIControlStateHighlighted:
updatedConfig.baseForegroundColor = kTitleColorStateHighlighted;
break;
case UIControlStateNormal:
updatedConfig.baseForegroundColor =
[UIColor colorNamed:kTextPrimaryColor];
break;
default:
break;
}
incomingButton.configuration = updatedConfig;
};
} else {
[button setTitleColor:kTitleColorStateNormal forState:UIControlStateNormal];
[button setTitleColor:kTitleColorStateHighlighted
forState:UIControlStateHighlighted];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorNamed:kTextPrimaryColor]
forState:UIControlStateNormal];
UIEdgeInsets contentEdgeInsets =
UIEdgeInsetsMake(0, kHorizontalEdgeInset, 0, kHorizontalEdgeInset);
SetContentEdgeInsets(button, contentEdgeInsets);
[button.titleLabel setFont:[UIFont systemFontOfSize:kButtonTitleFontSize
weight:UIFontWeightMedium]];
}
button.clipsToBounds = YES;
[button addTarget:self
action:@selector(keyboardButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
button.isAccessibilityElement = YES;
[button setAccessibilityLabel:title];
return button;
}
- (BOOL)enableInputClicksWhenVisible {
return YES;
}
- (void)keyboardButtonPressed:(id)sender {
UIButton* button = base::apple::ObjCCastStrict<UIButton>(sender);
[[UIDevice currentDevice] playInputClick];
if (IsUIButtonConfigurationEnabled()) {
[_delegate keyPressed:button.configuration.title];
} else {
[_delegate keyPressed:[button currentTitle]];
}
}
- (void)didMoveToWindow {
[super didMoveToWindow];
if (!self.window || ![self.textField isFirstResponder]) {
return;
}
// Log the Lens support status when the keyboard is opened.
lens_availability::CheckAndLogAvailabilityForLensEntryPoint(
LensEntrypoint::Keyboard,
[self isGoogleSearchEngine:self.templateURLService]);
}
#pragma mark - Setters
- (void)setTemplateURLService:(TemplateURLService*)templateURLService {
_templateURLService = templateURLService;
if (_templateURLService) {
_searchEngineObserver =
std::make_unique<SearchEngineObserverBridge>(self, templateURLService);
} else {
_searchEngineObserver.reset();
}
}
#pragma mark - SearchEngineObserving
- (void)searchEngineChanged {
// Regenerate the shortcut buttons depending on the new search engine.
[self addSubviews];
}
#pragma mark - Private
- (BOOL)isGoogleSearchEngine:(TemplateURLService*)service {
DCHECK(service);
const TemplateURL* defaultURL = service->GetDefaultSearchProvider();
return defaultURL &&
defaultURL->GetEngineType(service->search_terms_data()) ==
SEARCH_ENGINE_GOOGLE;
}
@end