| // 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/content_suggestions/content_suggestions_collection_utils.h" |
| |
| #import "base/i18n/rtl.h" |
| #import "components/strings/grit/components_strings.h" |
| #import "ios/chrome/browser/location_bar/ui_bundled/location_bar_constants.h" |
| #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h" |
| #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_constants.h" |
| #import "ios/chrome/browser/shared/public/features/features.h" |
| #import "ios/chrome/browser/shared/ui/elements/new_feature_badge_view.h" |
| #import "ios/chrome/browser/shared/ui/symbols/symbols.h" |
| #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h" |
| #import "ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h" |
| #import "ios/chrome/browser/toolbar/ui_bundled/public/toolbar_constants.h" |
| #import "ios/chrome/browser/toolbar/ui_bundled/public/toolbar_utils.h" |
| #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h" |
| #import "ios/chrome/common/ui/colors/semantic_color_names.h" |
| #import "ios/chrome/common/ui/util/pointer_interaction_util.h" |
| #import "ios/chrome/common/ui/util/ui_util.h" |
| #import "ios/chrome/grit/ios_branded_strings.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ios/components/ui_util/dynamic_type_util.h" |
| #import "ui/base/device_form_factor.h" |
| #import "ui/base/l10n/l10n_util.h" |
| #import "ui/gfx/ios/uikit_util.h" |
| |
| namespace { |
| |
| // Width of search field. |
| const CGFloat kSearchFieldLarge = 432; |
| const CGFloat kSearchFieldSmall = 343; |
| const CGFloat kSearchFieldSmallMin = 304; |
| const CGFloat kSearchFieldMinMargin = 8; |
| |
| const CGFloat kTopSpacingMaterial = 24; |
| |
| // Top margin for the doodle. |
| const CGFloat kDoodleTopMarginRegularXRegular = 162; |
| const CGFloat kDoodleTopMarginOther = 65; |
| // Size of the doodle top margin which is multiplied by the scaled font factor, |
| // and added to `kDoodleTopMarginOther` on non Regular x Regular form factors. |
| const CGFloat kDoodleScaledTopMarginOther = 10; |
| |
| // Top margin for the search field |
| const CGFloat kSearchFieldTopMargin = 22; |
| |
| // Bottom margin for the search field. |
| const CGFloat kNTPShrunkLogoSearchFieldBottomPadding = 20; |
| |
| // Height for the logo and doodle frame. |
| const CGFloat kGoogleSearchDoodleHeight = 120; |
| |
| // Height for the shrunk doodle frame. |
| // TODO(crbug.com/40744549): clean up post-launch. |
| const CGFloat kGoogleSearchDoodleShrunkHeight = 68; |
| |
| // Height for the shrunk logo frame. |
| // TODO(crbug.com/40744549): clean up post-launch. |
| const CGFloat kGoogleSearchLogoHeight = 36; |
| |
| // The size of the symbol image. |
| const CGFloat kSymbolContentSuggestionsPointSize = 18; |
| |
| // Constants for a symbol button with an new badge. |
| const CGFloat kSymbolButtonSize = 37.0; |
| const CGFloat kButtonShadowOpacity = 0.35; |
| const CGFloat kButtonShadowRadius = 1.0; |
| const CGFloat kButtonShadowVerticalOffset = 1.0; |
| const CGFloat kNewBadgeOffsetFromButtonCenter = 14.0; |
| |
| // Height and width of the new feature badge. |
| const CGFloat kNewFeatureBadgeSize = 20; |
| // Font size of the new feature badge label. |
| const CGFloat kNewFeatureFontSize = 10; |
| |
| // Returns the color to use for the Lens and Voice icons in the Fakebox. |
| UIColor* FakeboxIconColor() { |
| return [UIColor colorNamed:kGrey700Color]; |
| } |
| |
| // Sets up fakebox button with a round background and new badge view. |
| void SetUpButtonWithNewFeatureBadge(UIButton* button) { |
| [button setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| |
| button.backgroundColor = [UIColor colorNamed:kOmniboxKeyboardButtonColor]; |
| button.layer.cornerRadius = kSymbolButtonSize / 2; |
| |
| button.layer.shadowColor = [UIColor blackColor].CGColor; |
| button.layer.shadowOffset = CGSizeMake(0, kButtonShadowVerticalOffset); |
| button.layer.shadowOpacity = kButtonShadowOpacity; |
| button.layer.shadowRadius = kButtonShadowRadius; |
| |
| NewFeatureBadgeView* badgeView = |
| [[NewFeatureBadgeView alloc] initWithBadgeSize:kNewFeatureBadgeSize |
| fontSize:kNewFeatureFontSize]; |
| badgeView.translatesAutoresizingMaskIntoConstraints = NO; |
| badgeView.accessibilityElementsHidden = YES; |
| [button.imageView addSubview:badgeView]; |
| |
| [NSLayoutConstraint activateConstraints:@[ |
| [button.widthAnchor constraintEqualToConstant:kSymbolButtonSize], |
| [button.heightAnchor constraintEqualToConstant:kSymbolButtonSize], |
| [badgeView.centerXAnchor |
| constraintEqualToAnchor:button.imageView.centerXAnchor |
| constant:kNewBadgeOffsetFromButtonCenter], |
| [badgeView.centerYAnchor |
| constraintEqualToAnchor:button.imageView.centerYAnchor |
| constant:-kNewBadgeOffsetFromButtonCenter], |
| ]]; |
| } |
| } // namespace |
| |
| namespace content_suggestions { |
| |
| const CGFloat kHintTextScale = 0.15; |
| const CGFloat kReturnToRecentTabSectionBottomMargin = 25; |
| |
| CGFloat DoodleHeight(BOOL logo_is_showing, |
| BOOL doodle_is_showing, |
| UITraitCollection* trait_collection) { |
| // For users with non-Google default search engine, there is no doodle. |
| if (!IsRegularXRegularSizeClass(trait_collection) && !logo_is_showing) { |
| return 0; |
| } |
| |
| if (logo_is_showing) { |
| if (doodle_is_showing || |
| (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET)) { |
| return kGoogleSearchDoodleShrunkHeight; |
| } else { |
| return kGoogleSearchLogoHeight; |
| } |
| } |
| |
| return kGoogleSearchDoodleHeight; |
| } |
| |
| CGFloat DoodleTopMargin(CGFloat top_inset, |
| UITraitCollection* trait_collection) { |
| if (IsRegularXRegularSizeClass(trait_collection)) { |
| return kDoodleTopMarginRegularXRegular; |
| } |
| CGFloat top_margin = |
| top_inset + |
| AlignValueToPixel(kDoodleScaledTopMarginOther * |
| ui_util::SystemSuggestedFontSizeMultiplier()); |
| // If Magic Stack is not enabled, this value is zero (e.g. no-op). |
| top_margin -= ReducedNTPTopMarginSpaceForMagicStack(); |
| top_margin += kDoodleTopMarginOther; |
| top_margin += TopPaddingToNTP(); |
| return top_margin; |
| } |
| |
| CGFloat HeaderSeparatorHeight() { |
| return ui::AlignValueToUpperPixel(kToolbarSeparatorHeight); |
| } |
| |
| CGFloat SearchFieldTopMargin() { |
| return kSearchFieldTopMargin; |
| } |
| |
| CGFloat SearchFieldWidth(CGFloat width, UITraitCollection* trait_collection) { |
| if (!IsCompactWidth(trait_collection) && !IsCompactHeight(trait_collection)) { |
| return kSearchFieldLarge; |
| } |
| |
| // Special case for narrow sizes. |
| return std::max( |
| kSearchFieldSmallMin, |
| std::min(kSearchFieldSmall, width - kSearchFieldMinMargin * 2)); |
| } |
| |
| CGFloat FakeOmniboxHeight() { |
| return ToolbarExpandedHeight( |
| [UIApplication sharedApplication].preferredContentSizeCategory); |
| } |
| |
| CGFloat PinnedFakeOmniboxHeight() { |
| return LocationBarHeight( |
| [UIApplication sharedApplication].preferredContentSizeCategory); |
| } |
| |
| CGFloat FakeToolbarHeight() { |
| return ToolbarExpandedHeight( |
| [UIApplication sharedApplication].preferredContentSizeCategory); |
| } |
| |
| CGFloat HeightForLogoHeader(BOOL logo_is_showing, |
| BOOL doodle_is_showing, |
| UITraitCollection* trait_collection) { |
| CGFloat header_height = |
| DoodleTopMargin(0, trait_collection) + |
| DoodleHeight(logo_is_showing, doodle_is_showing, trait_collection) + |
| SearchFieldTopMargin() + FakeOmniboxHeight() + |
| ntp_header::kScrolledToTopOmniboxBottomMargin + |
| ceil(HeaderSeparatorHeight()); |
| if (!IsRegularXRegularSizeClass(trait_collection)) { |
| return header_height; |
| } |
| if (!logo_is_showing) { |
| // Returns sufficient vertical space for the Identity Disc to be |
| // displayed. |
| return ntp_home::kIdentityAvatarDimension + |
| 2 * (ntp_home::kHeaderIconMargin + ntp_home::kIdentityAvatarPadding); |
| } |
| |
| header_height += kTopSpacingMaterial; |
| |
| return header_height; |
| } |
| |
| CGFloat HeaderBottomPadding() { |
| return kNTPShrunkLogoSearchFieldBottomPadding; |
| } |
| |
| void ConfigureSearchHintLabel(UILabel* search_hint_label, |
| UIView* search_tab_target) { |
| [search_hint_label setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| [search_tab_target addSubview:search_hint_label]; |
| |
| [search_hint_label setText:l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT)]; |
| if (base::i18n::IsRTL()) { |
| [search_hint_label setTextAlignment:NSTextAlignmentRight]; |
| } |
| search_hint_label.textColor = SearchHintLabelColor(); |
| search_hint_label.adjustsFontForContentSizeCategory = YES; |
| search_hint_label.textAlignment = NSTextAlignmentCenter; |
| } |
| |
| void ConfigureVoiceSearchButton(UIButton* voice_search_button, |
| BOOL use_color_icon) { |
| [voice_search_button setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| |
| UIButtonConfiguration* buttonConfig = |
| [UIButtonConfiguration plainButtonConfiguration]; |
| buttonConfig.contentInsets = NSDirectionalEdgeInsetsMake(0, 0, 0, 0); |
| voice_search_button.configuration = buttonConfig; |
| |
| voice_search_button.tintColor = FakeboxIconColor(); |
| UIImage* mic_image = CustomSymbolWithPointSize( |
| kVoiceSymbol, kSymbolContentSuggestionsPointSize); |
| mic_image = use_color_icon ? MakeSymbolMulticolor(mic_image) |
| : MakeSymbolMonochrome(mic_image); |
| [voice_search_button setImage:mic_image forState:UIControlStateNormal]; |
| [voice_search_button setAccessibilityLabel:l10n_util::GetNSString( |
| IDS_IOS_ACCNAME_VOICE_SEARCH)]; |
| [voice_search_button setAccessibilityIdentifier:@"Voice Search"]; |
| |
| voice_search_button.pointerInteractionEnabled = YES; |
| // Make the pointer shape fit the location bar's semi-circle end shape. |
| voice_search_button.pointerStyleProvider = |
| CreateLiftEffectCirclePointerStyleProvider(); |
| } |
| |
| void ConfigureLensButtonAppearance(UIButton* lens_button, |
| BOOL use_new_badge, |
| BOOL use_color_icon) { |
| lens_button.translatesAutoresizingMaskIntoConstraints = NO; |
| |
| UIButtonConfiguration* buttonConfig = |
| [UIButtonConfiguration plainButtonConfiguration]; |
| buttonConfig.contentInsets = NSDirectionalEdgeInsetsMake(0, 0, 0, 0); |
| lens_button.configuration = buttonConfig; |
| lens_button.accessibilityLabel = l10n_util::GetNSString(IDS_IOS_ACCNAME_LENS); |
| lens_button.accessibilityIdentifier = @"Lens"; |
| |
| lens_button.pointerInteractionEnabled = YES; |
| // Make the pointer shape fit the location bar's semi-circle end shape. |
| lens_button.pointerStyleProvider = |
| CreateLiftEffectCirclePointerStyleProvider(); |
| |
| // Use a monochrome or colored symbol with no background. |
| UIImage* camera_image = CustomSymbolWithPointSize( |
| kCameraLensSymbol, kSymbolContentSuggestionsPointSize); |
| camera_image = use_color_icon ? MakeSymbolMulticolor(camera_image) |
| : MakeSymbolMonochrome(camera_image); |
| [lens_button setImage:camera_image forState:UIControlStateNormal]; |
| lens_button.tintColor = FakeboxIconColor(); |
| |
| if (use_new_badge) { |
| // Show the "New" badge and colored symbol. |
| SetUpButtonWithNewFeatureBadge(lens_button); |
| } |
| } |
| |
| void ConfigureLensButtonWithNewBadgeAlpha(UIButton* lens_button, |
| CGFloat new_badge_alpha) { |
| // Fade button background. |
| lens_button.backgroundColor = |
| [[UIColor colorNamed:kOmniboxKeyboardButtonColor] |
| colorWithAlphaComponent:new_badge_alpha]; |
| lens_button.layer.shadowOpacity = kButtonShadowOpacity * new_badge_alpha; |
| |
| // Scale the N badge. |
| for (UIView* subview in lens_button.imageView.subviews) { |
| subview.alpha = new_badge_alpha; |
| subview.transform = CGAffineTransformScale( |
| CGAffineTransformIdentity, new_badge_alpha, new_badge_alpha); |
| } |
| } |
| |
| UIView* NearestAncestor(UIView* view, Class of_class) { |
| if (!view) { |
| return nil; |
| } |
| if ([view isKindOfClass:of_class]) { |
| return view; |
| } |
| return NearestAncestor([view superview], of_class); |
| } |
| |
| UIColor* SearchHintLabelColor() { |
| return [UIColor colorNamed:kGrey800Color]; |
| } |
| |
| int SetUpListTitleStringID() { |
| return IsIOSTipsNotificationsEnabled() ? IDS_IOS_SET_UP_LIST_TIPS_TITLE |
| : IDS_IOS_SET_UP_LIST_TITLE; |
| } |
| |
| NSString* SetUpListTitleString() { |
| return l10n_util::GetNSString(SetUpListTitleStringID()); |
| } |
| |
| } // namespace content_suggestions |