Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.mm

Issue 2589803002: Upstream Chrome on iOS source code [6/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h"
6
7 #import <QuartzCore/QuartzCore.h>
8
9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/user_metrics.h"
13 #include "base/metrics/user_metrics_action.h"
14 #include "components/omnibox/browser/autocomplete_match.h"
15 #include "components/omnibox/browser/omnibox_edit_model.h"
16 #include "components/omnibox/browser/omnibox_popup_model.h"
17 #include "components/open_from_clipboard/clipboard_recent_content.h"
18 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
19 #import "ios/chrome/browser/experimental_flags.h"
20 #import "ios/chrome/browser/ui/omnibox/omnibox_popup_material_view_controller.h"
21 #import "ios/chrome/browser/ui/omnibox/omnibox_popup_positioner.h"
22 #include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
23 #include "ios/chrome/browser/ui/omnibox/omnibox_view_ios.h"
24 #include "ios/chrome/browser/ui/ui_util.h"
25 #include "ios/chrome/grit/ios_theme_resources.h"
26 #include "ios/web/public/image_fetcher/image_data_fetcher.h"
27 #include "ios/web/public/web_thread.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/geometry/rect.h"
31 #include "ui/gfx/image/image.h"
32
33 namespace {
34 const CGFloat kExpandAnimationDuration = 0.1;
35 const CGFloat kCollapseAnimationDuration = 0.05;
36 const CGFloat kWhiteBackgroundHeight = 74;
37 NS_INLINE CGFloat ShadowHeight() {
38 return IsIPadIdiom() ? 10 : 0;
39 }
40 } // namespace
41
42 using base::UserMetricsAction;
43
44 OmniboxPopupViewIOS::OmniboxPopupViewIOS(OmniboxViewIOS* edit_view,
45 OmniboxEditModel* edit_model,
46 id<OmniboxPopupPositioner> positioner)
47 : model_(new OmniboxPopupModel(this, edit_model)),
48 edit_view_(edit_view),
49 positioner_(positioner),
50 is_open_(false) {
51 DCHECK(edit_view);
52 DCHECK(edit_model);
53
54 std::unique_ptr<web::ImageDataFetcher> imageFetcher =
55 base::MakeUnique<web::ImageDataFetcher>(
56 web::WebThread::GetBlockingPool());
57 imageFetcher->SetRequestContextGetter(
58 edit_view->browser_state()->GetRequestContext());
59
60 popup_controller_.reset([[OmniboxPopupMaterialViewController alloc]
61 initWithPopupView:this
62 withFetcher:std::move(imageFetcher)]);
63 [popup_controller_ setIncognito:edit_view->browser_state()->IsOffTheRecord()];
64 popupView_.reset([[UIView alloc] initWithFrame:CGRectZero]);
65 [popupView_ setClipsToBounds:YES];
66 CALayer* popupLayer = [popupView_ layer];
67 // Adjust popupView_'s anchor point and height so that it animates down
68 // from the top when it appears.
69 popupLayer.anchorPoint = CGPointMake(0.5, 0);
70 UIView* popupControllerView = [popup_controller_ view];
71 CGRect popupControllerFrame = popupControllerView.frame;
72 popupControllerFrame.origin = CGPointZero;
73 popupControllerView.frame = popupControllerFrame;
74 [popupView_ addSubview:popupControllerView];
75 if (IsIPadIdiom()) {
76 [popupView_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
77 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
78 gfx::Image shadowImage =
79 rb.GetNativeImageNamed(IDR_IOS_TOOLBAR_SHADOW_FULL_BLEED);
80 base::scoped_nsobject<UIImageView> shadowView(
81 [[UIImageView alloc] initWithImage:shadowImage.ToUIImage()]);
82 [shadowView setUserInteractionEnabled:NO];
83 [shadowView setTranslatesAutoresizingMaskIntoConstraints:NO];
84 [popupView_ addSubview:shadowView];
85
86 // Add constraints to position |shadowView| at the bottom of |popupView_|
87 // with the same width as |popupView_|.
88 NSDictionary* views = NSDictionaryOfVariableBindings(shadowView);
89 [popupView_
90 addConstraints:[NSLayoutConstraint
91 constraintsWithVisualFormat:@"H:|[shadowView]|"
92 options:0
93 metrics:nil
94 views:views]];
95 [popupView_ addConstraint:[NSLayoutConstraint
96 constraintWithItem:shadowView
97 attribute:NSLayoutAttributeBottom
98 relatedBy:NSLayoutRelationEqual
99 toItem:popupView_
100 attribute:NSLayoutAttributeBottom
101 multiplier:1
102 constant:0]];
103 } else {
104 // Add a white background to prevent seing the logo scroll through the
105 // omnibox.
106 base::scoped_nsobject<UIView> whiteBackground(
107 [[UIView alloc] initWithFrame:CGRectZero]);
108 [popupView_ addSubview:whiteBackground];
109 [whiteBackground setBackgroundColor:[UIColor whiteColor]];
110
111 // Set constraints to |whiteBackground|.
112 [whiteBackground setTranslatesAutoresizingMaskIntoConstraints:NO];
113 NSDictionary* metrics = @{ @"height" : @(kWhiteBackgroundHeight) };
114 NSDictionary* views = NSDictionaryOfVariableBindings(whiteBackground);
115 [popupView_
116 addConstraints:[NSLayoutConstraint
117 constraintsWithVisualFormat:@"H:|[whiteBackground]|"
118 options:0
119 metrics:nil
120 views:views]];
121 [popupView_
122 addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
123 @"V:[whiteBackground(==height)]"
124 options:0
125 metrics:metrics
126 views:views]];
127 [popupView_ addConstraint:[NSLayoutConstraint
128 constraintWithItem:whiteBackground
129 attribute:NSLayoutAttributeBottom
130 relatedBy:NSLayoutRelationEqual
131 toItem:popupView_
132 attribute:NSLayoutAttributeTop
133 multiplier:1
134 constant:0]];
135 // |whiteBackground| extends out of |popupView_|
136 [popupView_ setClipsToBounds:NO];
137 }
138 }
139
140 OmniboxPopupViewIOS::~OmniboxPopupViewIOS() {
141 // Destroy the model, in case it tries to call back into us when destroyed.
142 model_.reset();
143 }
144
145 // Set left image to globe or magnifying glass depending on which autocomplete
146 // option comes first.
147 void OmniboxPopupViewIOS::UpdateEditViewIcon() {
148 const AutocompleteResult& result = model_->result();
149 const AutocompleteMatch& match = result.match_at(0); // 0 for first result.
150 int image_id = GetIconForAutocompleteMatchType(
151 match.type, /* is_starred */ false, /* is_incognito */ false);
152 edit_view_->SetLeftImage(image_id);
153 }
154
155 void OmniboxPopupViewIOS::UpdatePopupAppearance() {
156 const AutocompleteResult& result = model_->result();
157 UIView* view = popupView_;
158
159 if (!is_open_ && !result.empty()) {
160 // The popup is not currently open and there are results to display. Update
161 // and animate the cells
162 [popup_controller_ updateMatches:result withAnimation:YES];
163 } else {
164 // The popup is already displayed or there are no results to display. Update
165 // the cells without animating.
166 [popup_controller_ updateMatches:result withAnimation:NO];
167 }
168 is_open_ = !result.empty();
169
170 if (is_open_) {
171 // Show |result.size| on iPad. Since iPhone can dismiss keyboard, set
172 // height to frame height.
173 CGFloat height = [[popup_controller_ tableView] contentSize].height;
174 UIEdgeInsets insets = [[popup_controller_ tableView] contentInset];
175 // Note the calculation |insets.top * 2| is correct, it should not be
176 // insets.top + insets.bottom. |insets.bottom| will be larger than
177 // |insets.top| when the keyboard is visible, but |parentHeight| should stay
178 // the same.
179 CGFloat parentHeight = height + insets.top * 2 + ShadowHeight();
180 UIView* siblingView = [positioner_ popupAnchorView];
181 if (!IsIPadIdiom()) {
182 [view setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
183 UIViewAutoresizingFlexibleHeight];
184 [[siblingView superview] insertSubview:view belowSubview:siblingView];
185 } else {
186 [[siblingView superview] insertSubview:view aboveSubview:siblingView];
187 }
188 CGFloat currentHeight = view.layer.bounds.size.height;
189 if (currentHeight == 0)
190 AnimateDropdownExpansion(parentHeight);
191 else
192 [view setFrame:[positioner_ popupFrame:parentHeight]];
193 UIView* popupControllerView = [popup_controller_ view];
194 CGRect popupControllerFrame = popupControllerView.frame;
195 popupControllerFrame.size.height = view.frame.size.height - ShadowHeight();
196 popupControllerView.frame = popupControllerFrame;
197 UpdateEditViewIcon();
198 } else {
199 AnimateDropdownCollapse();
200 }
201
202 edit_view_->OnPopupResultsChanged(result);
203 }
204
205 void OmniboxPopupViewIOS::AnimateDropdownExpansion(CGFloat parentHeight) {
206 CGRect popupFrame = [positioner_ popupFrame:parentHeight];
207 CALayer* popupLayer = [popupView_ layer];
208 CGRect bounds = popupLayer.bounds;
209 bounds.size.height = popupFrame.size.height;
210 popupLayer.bounds = bounds;
211
212 CGRect frame = [popupView_ frame];
213 frame.size.width = popupFrame.size.width;
214 frame.origin.y = popupFrame.origin.y;
215 [popupView_ setFrame:frame];
216
217 CABasicAnimation* growHeight =
218 [CABasicAnimation animationWithKeyPath:@"bounds.size.height"];
219 growHeight.fromValue = @0;
220 growHeight.toValue = [NSNumber numberWithFloat:popupFrame.size.height];
221 growHeight.duration = kExpandAnimationDuration;
222 growHeight.timingFunction =
223 [CAMediaTimingFunction functionWithControlPoints:0.4:0:0.2:1];
224 [popupLayer addAnimation:growHeight forKey:@"growHeight"];
225 }
226
227 void OmniboxPopupViewIOS::AnimateDropdownCollapse() {
228 CALayer* popupLayer = [popupView_ layer];
229 CGRect bounds = popupLayer.bounds;
230 CGFloat currentHeight = bounds.size.height;
231 bounds.size.height = 0;
232 popupLayer.bounds = bounds;
233
234 UIView* retainedPopupView = popupView_;
235 [CATransaction begin];
236 [CATransaction setCompletionBlock:^{
237 [retainedPopupView removeFromSuperview];
238 }];
239 CABasicAnimation* shrinkHeight =
240 [CABasicAnimation animationWithKeyPath:@"bounds.size.height"];
241 shrinkHeight.fromValue = [NSNumber numberWithFloat:currentHeight];
242 shrinkHeight.toValue = @0;
243 shrinkHeight.duration = kCollapseAnimationDuration;
244 shrinkHeight.timingFunction =
245 [CAMediaTimingFunction functionWithControlPoints:0.4:0:1:1];
246 [popupLayer addAnimation:shrinkHeight forKey:@"shrinkHeight"];
247 [CATransaction commit];
248 }
249
250 gfx::Rect OmniboxPopupViewIOS::GetTargetBounds() {
251 return gfx::Rect();
252 }
253
254 // For phone, allow popup to take focus (and dismiss the keyboard) on scroll.
255 void OmniboxPopupViewIOS::DidScroll() {
256 if (!IsIPadIdiom()) {
257 edit_view_->HideKeyboard();
258 }
259 }
260
261 // Puts omnibox back into focus with suggested search terms.
262 void OmniboxPopupViewIOS::CopyToOmnibox(const base::string16& str) {
263 edit_view_->SetUserText(str);
264 edit_view_->FocusOmnibox();
265 }
266
267 void OmniboxPopupViewIOS::SetTextAlignment(NSTextAlignment alignment) {
268 [popup_controller_ setTextAlignment:alignment];
269 }
270
271 bool OmniboxPopupViewIOS::IsStarredMatch(const AutocompleteMatch& match) const {
272 return model_->IsStarredMatch(match);
273 }
274
275 void OmniboxPopupViewIOS::DeleteMatch(const AutocompleteMatch& match) const {
276 model_->autocomplete_controller()->DeleteMatch(match);
277 }
278
279 void OmniboxPopupViewIOS::OpenURLForRow(size_t row) {
280 // Crash reports tell us that |row| is sometimes indexed past the end of
281 // the results array. In those cases, just ignore the request and return
282 // early. See b/5813291.
283 if (row >= model_->result().size())
284 return;
285
286 WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB;
287 base::RecordAction(UserMetricsAction("MobileOmniboxUse"));
288
289 // OpenMatch() may close the popup, which will clear the result set and, by
290 // extension, |match| and its contents. So copy the relevant match out to
291 // make sure it stays alive until the call completes.
292 AutocompleteMatch match = model_->result().match_at(row);
293 if (match.type == AutocompleteMatchType::CLIPBOARD) {
294 base::RecordAction(UserMetricsAction("MobileOmniboxClipboardToURL"));
295 UMA_HISTOGRAM_LONG_TIMES_100(
296 "MobileOmnibox.PressedClipboardSuggestionAge",
297 ClipboardRecentContent::GetInstance()->GetClipboardContentAge());
298 }
299 edit_view_->OpenMatch(match, disposition, GURL(), base::string16(), row);
300 }
301
302 bool OmniboxPopupViewIOS::IsOpen() const {
303 return is_open_;
304 }
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h ('k') | ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698