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

Side by Side Diff: chrome/browser/ui/views/omnibox/inline_omnibox_popup_view.cc

Issue 10580039: Adds ability to render omnibox as a view above the page. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: cleanup Created 8 years, 6 months 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 | Annotate | Revision Log
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 "chrome/browser/ui/views/omnibox/inline_omnibox_popup_view.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/themes/theme_service.h"
tfarina 2012/06/21 06:38:37 nit: is this used in this file?
12 #include "chrome/browser/ui/omnibox/omnibox_view.h"
13 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
14 #include "chrome/browser/ui/views/omnibox/omnibox_result_view.h"
15 #include "chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.h"
tfarina 2012/06/21 06:38:37 nit: this isn't used here? can we remove this incl
16 #include "grit/chromium_strings.h"
17 #include "grit/generated_resources.h"
18 #include "grit/theme_resources.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/layout.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/base/theme_provider.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/insets.h"
25 #include "ui/gfx/path.h"
26 #include "ui/views/background.h"
27 #include "ui/views/controls/button/text_button.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/layout_constants.h"
30 #include "ui/views/painter.h"
31 #include "ui/views/widget/widget.h"
32 #include "unicode/ubidi.h"
33
34 namespace {
35
36 // The size delta between the font used for the edit and the result rows. Passed
37 // to gfx::Font::DeriveFont.
38 #if defined(OS_CHROMEOS)
39 // Don't adjust the size on Chrome OS (http://crbug.com/61433).
40 const int kEditFontAdjust = 0;
41 #else
42 const int kEditFontAdjust = -1;
43 #endif
44
45 } // namespace
46
47 InlineOmniboxPopupView::InlineOmniboxPopupView(
48 const gfx::Font& font,
49 OmniboxView* omnibox_view,
50 AutocompleteEditModel* edit_model,
51 views::View* location_bar)
52 : model_(new AutocompletePopupModel(this, edit_model)),
53 omnibox_view_(omnibox_view),
54 profile_(edit_model->profile()),
55 location_bar_(location_bar),
56 result_font_(font.DeriveFont(kEditFontAdjust)),
57 result_bold_font_(result_font_.DeriveFont(0, gfx::Font::BOLD)),
58 ignore_mouse_drag_(false),
59 ALLOW_THIS_IN_INITIALIZER_LIST(size_animation_(this)) {
60 // We're owned by LocationBarView.
61 set_owned_by_client();
62 // Our visibility determines whether the popup is open. Start out closed.
63 SetVisible(false);
64 // TODO(sky): replace with constant kNTPBackgroundColor.
65 set_background(views::Background::CreateSolidBackground(
66 SkColorSetRGB(0xF5, 0xF5, 0xF5)));
67 }
68
69 InlineOmniboxPopupView::~InlineOmniboxPopupView() {
70 }
71
72 void InlineOmniboxPopupView::Init() {
73 // This can't be done in the constructor as at that point we aren't
74 // necessarily our final class yet, and we may have subclasses
75 // overriding CreateResultView.
76 for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) {
77 OmniboxResultView* result_view =
78 CreateResultView(this, i, result_font_, result_bold_font_);
79 result_view->SetVisible(false);
80 AddChildViewAt(result_view, static_cast<int>(i));
81 }
82 }
83
84 gfx::Rect InlineOmniboxPopupView::GetPopupBounds() const {
85 if (!size_animation_.is_animating())
86 return target_bounds_;
87
88 gfx::Rect current_frame_bounds = start_bounds_;
89 int total_height_delta = target_bounds_.height() - start_bounds_.height();
90 // Round |current_height_delta| instead of truncating so we won't leave single
91 // white pixels at the bottom of the popup as long when animating very small
92 // height differences.
93 int current_height_delta = static_cast<int>(
94 size_animation_.GetCurrentValue() * total_height_delta - 0.5);
95 current_frame_bounds.set_height(
96 current_frame_bounds.height() + current_height_delta);
97 return current_frame_bounds;
98 }
99
100 void InlineOmniboxPopupView::LayoutChildren() {
101 // Make the children line up with the LocationBar.
102 gfx::Point content_origin;
103 // TODO(sky): this won't work correctly with LocationBarContainer.
104 views::View::ConvertPointToView(location_bar_, this, &content_origin);
105 int x = content_origin.x();
106 int width = location_bar_->width();
107 gfx::Rect contents_rect = GetContentsBounds();
108 int top = contents_rect.y();
109 for (int i = 0; i < child_count(); ++i) {
110 View* v = child_at(i);
111 if (v->visible()) {
112 v->SetBounds(x, top, width, v->GetPreferredSize().height());
113 top = v->bounds().bottom();
114 }
115 }
116 }
117
118 ////////////////////////////////////////////////////////////////////////////////
119 // InlineOmniboxPopupView, AutocompletePopupView overrides:
120
121 bool InlineOmniboxPopupView::IsOpen() const {
122 return visible();
123 }
124
125 void InlineOmniboxPopupView::InvalidateLine(size_t line) {
126 OmniboxResultView* result = static_cast<OmniboxResultView*>(
127 child_at(static_cast<int>(line)));
128 result->Invalidate();
129
130 if (HasMatchAt(line) && GetMatchAtIndex(line).associated_keyword.get()) {
131 result->ShowKeyword(IsSelectedIndex(line) &&
132 model_->selected_line_state() == AutocompletePopupModel::KEYWORD);
133 }
134 }
135
136 void InlineOmniboxPopupView::UpdatePopupAppearance() {
137 if (model_->result().empty()) {
138 SetVisible(false);
139 PreferredSizeChanged();
140 return;
141 }
142
143 // Update the match cached by each row, in the process of doing so make sure
144 // we have enough row views.
145 size_t child_rv_count = child_count();
146 const size_t result_size = model_->result().size();
147 for (size_t i = 0; i < result_size; ++i) {
148 OmniboxResultView* view = static_cast<OmniboxResultView*>(child_at(i));
149 view->SetMatch(GetMatchAtIndex(i));
150 view->SetVisible(true);
151 }
152 for (size_t i = result_size; i < child_rv_count; ++i)
153 child_at(i)->SetVisible(false);
154
155 gfx::Rect new_target_bounds = CalculateTargetBounds(CalculatePopupHeight());
156
157 // If we're animating and our target height changes, reset the animation.
158 // NOTE: If we just reset blindly on _every_ update, then when the user types
159 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the
160 // last few pixels to get to one visible result.
161 if (new_target_bounds.height() != target_bounds_.height()) {
162 size_animation_.Reset();
163 PreferredSizeChanged();
164 }
165 target_bounds_ = new_target_bounds;
166
167 if (!visible()) {
168 SetVisible(true);
169 PreferredSizeChanged();
170 } else {
171 // Animate the popup shrinking, but don't animate growing larger since that
172 // would make the popup feel less responsive.
173 start_bounds_ = bounds();
174 if (target_bounds_.height() < start_bounds_.height())
175 size_animation_.Show();
176 else
177 start_bounds_ = target_bounds_;
178 if (GetPopupBounds().height() != bounds().height())
179 PreferredSizeChanged();
180 }
181
182 SchedulePaint();
183 }
184
185 gfx::Rect InlineOmniboxPopupView::GetTargetBounds() {
186 // Our bounds never obscure the page, so we return an empty rect.
187 return gfx::Rect();
188 }
189
190 void InlineOmniboxPopupView::PaintUpdatesNow() {
191 // TODO(beng): remove this from the interface.
192 }
193
194 void InlineOmniboxPopupView::OnDragCanceled() {
195 ignore_mouse_drag_ = true;
196 }
197
198 ////////////////////////////////////////////////////////////////////////////////
199 // InlineOmniboxPopupView, OmniboxResultViewModel implementation:
200
201 bool InlineOmniboxPopupView::IsSelectedIndex(size_t index) const {
202 return index == model_->selected_line();
203 }
204
205 bool InlineOmniboxPopupView::IsHoveredIndex(size_t index) const {
206 return index == model_->hovered_line();
207 }
208
209 const SkBitmap* InlineOmniboxPopupView::GetIconIfExtensionMatch(
210 size_t index) const {
211 if (!HasMatchAt(index))
tfarina 2012/06/21 06:38:37 nit: may be this: if (HasMatchAt(index)) return
sky 2012/06/21 18:34:24 I'm keeping this as is for easy comparison with Om
212 return NULL;
213 return model_->GetIconIfExtensionMatch(GetMatchAtIndex(index));
214 }
215
216 ////////////////////////////////////////////////////////////////////////////////
217 // InlineOmniboxPopupView, AnimationDelegate implementation:
218
219 void InlineOmniboxPopupView::AnimationProgressed(
220 const ui::Animation* animation) {
221 // We should only be running the animation when the popup is already visible.
222 DCHECK(visible());
223 PreferredSizeChanged();
224 }
225
226 ////////////////////////////////////////////////////////////////////////////////
227 // InlineOmniboxPopupView, views::View overrides:
228
229 gfx::Size InlineOmniboxPopupView::GetPreferredSize() {
230 return GetPopupBounds().size();
231 }
232
233 void InlineOmniboxPopupView::Layout() {
234 // Size our children to the available content area.
235 LayoutChildren();
236 }
237
238 views::View* InlineOmniboxPopupView::GetEventHandlerForPoint(
239 const gfx::Point& point) {
240 return this;
241 }
242
243 bool InlineOmniboxPopupView::OnMousePressed(
244 const views::MouseEvent& event) {
245 ignore_mouse_drag_ = false; // See comment on |ignore_mouse_drag_| in header.
246 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton())
247 UpdateLineEvent(event, event.IsLeftMouseButton());
248 return true;
249 }
250
251 bool InlineOmniboxPopupView::OnMouseDragged(
252 const views::MouseEvent& event) {
253 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton())
254 UpdateLineEvent(event, !ignore_mouse_drag_ && event.IsLeftMouseButton());
255 return true;
256 }
257
258 void InlineOmniboxPopupView::OnMouseReleased(
259 const views::MouseEvent& event) {
260 if (ignore_mouse_drag_) {
261 OnMouseCaptureLost();
262 return;
263 }
264
265 if (event.IsOnlyMiddleMouseButton() || event.IsOnlyLeftMouseButton()) {
266 OpenSelectedLine(event, event.IsOnlyLeftMouseButton() ? CURRENT_TAB :
tfarina 2012/06/21 06:38:37 Peter prefers: OpenSelectedLine(event, event.IsOnl
sky 2012/06/21 18:34:24 I'm keeping this as is for easy comparison with Om
267 NEW_BACKGROUND_TAB);
268 }
269 }
270
271 void InlineOmniboxPopupView::OnMouseCaptureLost() {
272 ignore_mouse_drag_ = false;
273 }
274
275 void InlineOmniboxPopupView::OnMouseMoved(
276 const views::MouseEvent& event) {
277 model_->SetHoveredLine(GetIndexForPoint(event.location()));
278 }
279
280 void InlineOmniboxPopupView::OnMouseEntered(
281 const views::MouseEvent& event) {
282 model_->SetHoveredLine(GetIndexForPoint(event.location()));
283 }
284
285 void InlineOmniboxPopupView::OnMouseExited(
286 const views::MouseEvent& event) {
287 model_->SetHoveredLine(AutocompletePopupModel::kNoMatch);
288 }
289
290 ui::GestureStatus InlineOmniboxPopupView::OnGestureEvent(
291 const views::GestureEvent& event) {
292 switch (event.type()) {
293 case ui::ET_GESTURE_TAP_DOWN:
294 case ui::ET_GESTURE_SCROLL_BEGIN:
295 case ui::ET_GESTURE_SCROLL_UPDATE:
296 UpdateLineEvent(event, true);
297 break;
298 case ui::ET_GESTURE_TAP:
299 case ui::ET_GESTURE_SCROLL_END:
300 OpenSelectedLine(event, CURRENT_TAB);
301 break;
302 default:
303 return ui::GESTURE_STATUS_UNKNOWN;
304 }
305 return ui::GESTURE_STATUS_CONSUMED;
306 }
307
308 ////////////////////////////////////////////////////////////////////////////////
309 // InlineOmniboxPopupView, protected:
310
311 int InlineOmniboxPopupView::CalculatePopupHeight() {
312 DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size());
313 int popup_height = 0;
314 for (size_t i = 0; i < model_->result().size(); ++i)
315 popup_height += child_at(i)->GetPreferredSize().height();
316 return popup_height;
317 }
318
319 OmniboxResultView* InlineOmniboxPopupView::CreateResultView(
320 OmniboxResultViewModel* model,
321 int model_index,
322 const gfx::Font& font,
323 const gfx::Font& bold_font) {
324 return new OmniboxResultView(model, model_index, font, bold_font);
325 }
326
327 ////////////////////////////////////////////////////////////////////////////////
328 // InlineOmniboxPopupView, private:
329
330 bool InlineOmniboxPopupView::HasMatchAt(size_t index) const {
331 return index < model_->result().size();
332 }
333
334 const AutocompleteMatch& InlineOmniboxPopupView::GetMatchAtIndex(
335 size_t index) const {
336 return model_->result().match_at(index);
337 }
338
339 void InlineOmniboxPopupView::MakeContentsPath(
340 gfx::Path* path,
341 const gfx::Rect& bounding_rect) {
342 SkRect rect;
343 rect.set(SkIntToScalar(bounding_rect.x()),
344 SkIntToScalar(bounding_rect.y()),
345 SkIntToScalar(bounding_rect.right()),
346 SkIntToScalar(bounding_rect.bottom()));
347
348 SkScalar radius = SkIntToScalar(views::BubbleBorder::GetCornerRadius());
349 path->addRoundRect(rect, radius, radius);
350 }
351
352 void InlineOmniboxPopupView::OpenIndex(size_t index,
353 WindowOpenDisposition disposition) {
tfarina 2012/06/21 06:38:37 nit: wrong indentation here.
354 if (!HasMatchAt(index))
355 return;
356
357 // OpenMatch() may close the popup, which will clear the result set and, by
358 // extension, |match| and its contents. So copy the relevant match out to
359 // make sure it stays alive until the call completes.
360 AutocompleteMatch match = model_->result().match_at(index);
361 omnibox_view_->OpenMatch(match, disposition, GURL(), index);
362 }
363
364 size_t InlineOmniboxPopupView::GetIndexForPoint(
365 const gfx::Point& point) {
tfarina 2012/06/21 06:38:37 nit: does it will fit on the above line?
366 if (!HitTest(point))
367 return AutocompletePopupModel::kNoMatch;
368
369 int nb_match = model_->result().size();
370 DCHECK(nb_match <= child_count());
371 for (int i = 0; i < nb_match; ++i) {
372 views::View* child = child_at(i);
373 gfx::Point point_in_child_coords(point);
374 View::ConvertPointToView(this, child, &point_in_child_coords);
375 if (child->HitTest(point_in_child_coords))
376 return i;
377 }
378 return AutocompletePopupModel::kNoMatch;
379 }
380
381 gfx::Rect InlineOmniboxPopupView::CalculateTargetBounds(int h) {
382 return gfx::Rect(0, 0, 1, h);
383 }
384
385 void InlineOmniboxPopupView::UpdateLineEvent(
386 const views::LocatedEvent& event,
387 bool should_set_selected_line) {
388 size_t index = GetIndexForPoint(event.location());
389 model_->SetHoveredLine(index);
390 if (HasMatchAt(index) && should_set_selected_line)
391 model_->SetSelectedLine(index, false, false);
392 }
393
394 void InlineOmniboxPopupView::OpenSelectedLine(
395 const views::LocatedEvent& event,
396 WindowOpenDisposition disposition) {
397 size_t index = GetIndexForPoint(event.location());
398 OpenIndex(index, disposition);
tfarina 2012/06/21 06:38:37 nit: can we avoid the temp variable here?
sky 2012/06/21 18:34:24 Same comment about comparison.
399 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698