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

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

Issue 2543473005: Draw omnibox shadow with a ninebox layer.
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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h" 5 #include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
10 #include "base/macros.h" 10 #include "base/macros.h"
11 #include "build/build_config.h" 11 #include "build/build_config.h"
12 #include "chrome/browser/search/search.h" 12 #include "chrome/browser/search/search.h"
13 #include "chrome/browser/themes/theme_properties.h" 13 #include "chrome/browser/themes/theme_properties.h"
14 #include "chrome/browser/ui/layout_constants.h" 14 #include "chrome/browser/ui/layout_constants.h"
15 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 15 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
16 #include "chrome/browser/ui/views/omnibox/omnibox_result_view.h" 16 #include "chrome/browser/ui/views/omnibox/omnibox_result_view.h"
17 #include "chrome/browser/ui/views/theme_copying_widget.h" 17 #include "chrome/browser/ui/views/theme_copying_widget.h"
18 #include "components/omnibox/browser/omnibox_view.h" 18 #include "components/omnibox/browser/omnibox_view.h"
19 #include "third_party/skia/include/core/SkDrawLooper.h" 19 #include "third_party/skia/include/core/SkDrawLooper.h"
20 #include "ui/base/theme_provider.h" 20 #include "ui/base/theme_provider.h"
21 #include "ui/compositor/clip_recorder.h" 21 #include "ui/compositor/clip_recorder.h"
22 #include "ui/compositor/paint_recorder.h" 22 #include "ui/compositor/paint_recorder.h"
23 #include "ui/gfx/canvas.h" 23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/geometry/safe_integer_conversions.h" 24 #include "ui/gfx/geometry/safe_integer_conversions.h"
25 #include "ui/gfx/image/canvas_image_source.h"
25 #include "ui/gfx/image/image.h" 26 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/image/image_skia_operations.h" 27 #include "ui/gfx/image/image_skia_operations.h"
27 #include "ui/gfx/path.h" 28 #include "ui/gfx/path.h"
28 #include "ui/gfx/shadow_value.h" 29 #include "ui/gfx/shadow_value.h"
29 #include "ui/views/controls/image_view.h" 30 #include "ui/views/controls/image_view.h"
30 #include "ui/views/view_targeter.h" 31 #include "ui/views/view_targeter.h"
31 #include "ui/views/widget/widget.h" 32 #include "ui/views/widget/widget.h"
32 #include "ui/views/window/non_client_view.h" 33 #include "ui/views/window/non_client_view.h"
33 34
34 namespace { 35 namespace {
35 36
36 // Cache the shadow images so that potentially expensive shadow drawing isn't 37 // This source takes two images and draws one above the other.
38 class StackedImagesImageSource : public gfx::CanvasImageSource {
39 public:
40 StackedImagesImageSource(const gfx::ImageSkia& top,
41 const gfx::ImageSkia& bottom)
42 : CanvasImageSource(
43 gfx::Size(top.width(), top.height() + bottom.height()),
44 false),
45 top_(top),
46 bottom_(bottom) {
47 DCHECK_EQ(top.width(), bottom.width());
48 }
49 ~StackedImagesImageSource() override {}
50
51 // CanvasImageSource overrides:
52 void Draw(gfx::Canvas* canvas) override {
53 canvas->DrawImageInt(top_, 0, 0);
54 canvas->DrawImageInt(bottom_, 0, top_.height());
55 }
56
57 private:
58 const gfx::ImageSkia top_;
59 const gfx::ImageSkia bottom_;
60
61 DISALLOW_COPY_AND_ASSIGN(StackedImagesImageSource);
62 };
63
64 // Cache the shadow image so that potentially expensive shadow drawing isn't
37 // repeated. 65 // repeated.
38 base::LazyInstance<gfx::ImageSkia> g_top_shadow = LAZY_INSTANCE_INITIALIZER; 66 static int g_top_shadow_height = 0;
39 base::LazyInstance<gfx::ImageSkia> g_bottom_shadow = LAZY_INSTANCE_INITIALIZER; 67 static int g_bottom_shadow_height = 0;
68 base::LazyInstance<gfx::ImageSkia> g_shadow_ninebox = LAZY_INSTANCE_INITIALIZER;
40 69
41 } // namespace 70 } // namespace
42 71
43 class OmniboxPopupContentsView::AutocompletePopupWidget 72 class OmniboxPopupContentsView::AutocompletePopupWidget
44 : public ThemeCopyingWidget, 73 : public ThemeCopyingWidget,
45 public base::SupportsWeakPtr<AutocompletePopupWidget> { 74 public base::SupportsWeakPtr<AutocompletePopupWidget> {
46 public: 75 public:
47 explicit AutocompletePopupWidget(views::Widget* role_model) 76 explicit AutocompletePopupWidget(views::Widget* role_model)
48 : ThemeCopyingWidget(role_model) {} 77 : ThemeCopyingWidget(role_model) {}
49 ~AutocompletePopupWidget() override {} 78 ~AutocompletePopupWidget() override {}
(...skipping 26 matching lines...) Expand all
76 omnibox_view_(omnibox_view), 105 omnibox_view_(omnibox_view),
77 location_bar_view_(location_bar_view), 106 location_bar_view_(location_bar_view),
78 font_list_(font_list), 107 font_list_(font_list),
79 ignore_mouse_drag_(false), 108 ignore_mouse_drag_(false),
80 size_animation_(this), 109 size_animation_(this),
81 start_margin_(0), 110 start_margin_(0),
82 end_margin_(0) { 111 end_margin_(0) {
83 // The contents is owned by the LocationBarView. 112 // The contents is owned by the LocationBarView.
84 set_owned_by_client(); 113 set_owned_by_client();
85 114
86 if (g_top_shadow.Get().isNull()) { 115 if (g_shadow_ninebox.Get().isNull()) {
87 std::vector<gfx::ShadowValue> shadows; 116 std::vector<gfx::ShadowValue> top_shadows;
88 // Blur by 1dp. See comment below about blur accounting. 117 // Blur by 1dp. See comment below about blur accounting.
89 shadows.emplace_back(gfx::Vector2d(), 2, SK_ColorBLACK); 118 top_shadows.emplace_back(gfx::Vector2d(), 2, SK_ColorBLACK);
90 g_top_shadow.Get() = 119 gfx::ImageSkia top_shadow_image =
91 gfx::ImageSkiaOperations::CreateHorizontalShadow(shadows, false); 120 gfx::ImageSkiaOperations::CreateHorizontalShadow(top_shadows, false);
92 } 121 g_top_shadow_height = top_shadow_image.height();
93 if (g_bottom_shadow.Get().isNull()) { 122
123 // Values for the bottom shadow.
94 const int kSmallShadowBlur = 3; 124 const int kSmallShadowBlur = 3;
95 const int kLargeShadowBlur = 8; 125 const int kLargeShadowBlur = 8;
96 const int kLargeShadowYOffset = 3; 126 const int kLargeShadowYOffset = 3;
97 127
98 std::vector<gfx::ShadowValue> shadows; 128 std::vector<gfx::ShadowValue> bottom_shadows;
99 // gfx::ShadowValue counts blur pixels both inside and outside the shape, 129 // gfx::ShadowValue counts blur pixels both inside and outside the shape,
100 // whereas these blur values only describe the outside portion, hence they 130 // whereas these blur values only describe the outside portion, hence they
101 // must be doubled. 131 // must be doubled.
102 shadows.emplace_back(gfx::Vector2d(), 2 * kSmallShadowBlur, 132 bottom_shadows.emplace_back(gfx::Vector2d(), 2 * kSmallShadowBlur,
103 SK_ColorBLACK); 133 SK_ColorBLACK);
104 shadows.emplace_back(gfx::Vector2d(0, kLargeShadowYOffset), 134 bottom_shadows.emplace_back(gfx::Vector2d(0, kLargeShadowYOffset),
105 2 * kLargeShadowBlur, SK_ColorBLACK); 135 2 * kLargeShadowBlur, SK_ColorBLACK);
106 136
107 g_bottom_shadow.Get() = 137 gfx::ImageSkia bottom_shadow_image =
108 gfx::ImageSkiaOperations::CreateHorizontalShadow(shadows, true); 138 gfx::ImageSkiaOperations::CreateHorizontalShadow(bottom_shadows, true);
139 g_bottom_shadow_height = bottom_shadow_image.height();
140 auto source =
141 new StackedImagesImageSource(top_shadow_image, bottom_shadow_image);
142 g_shadow_ninebox.Get() = gfx::ImageSkia(source, source->size());
109 } 143 }
144 shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH));
145 shadow_layer_->SetFillsBoundsOpaquely(false);
146 shadow_layer_->UpdateNinePatchLayerImage(g_shadow_ninebox.Get());
147 shadow_layer_->UpdateNinePatchLayerAperture(
148 gfx::Rect(0, g_top_shadow_height, 1, 0));
149 shadow_layer_->UpdateNinePatchLayerBorder(gfx::Rect(
150 0, g_top_shadow_height, 0, g_top_shadow_height + g_bottom_shadow_height));
110 151
111 SetEventTargeter( 152 SetEventTargeter(
112 std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); 153 std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
113 } 154 }
114 155
115 void OmniboxPopupContentsView::Init() { 156 void OmniboxPopupContentsView::Init() {
116 // This can't be done in the constructor as at that point we aren't 157 // This can't be done in the constructor as at that point we aren't
117 // necessarily our final class yet, and we may have subclasses 158 // necessarily our final class yet, and we may have subclasses
118 // overriding CreateResultView. 159 // overriding CreateResultView.
119 for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) { 160 for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) {
(...skipping 21 matching lines...) Expand all
141 int current_height_delta = static_cast<int>( 182 int current_height_delta = static_cast<int>(
142 size_animation_.GetCurrentValue() * total_height_delta - 0.5); 183 size_animation_.GetCurrentValue() * total_height_delta - 0.5);
143 current_frame_bounds.set_height( 184 current_frame_bounds.set_height(
144 current_frame_bounds.height() + current_height_delta); 185 current_frame_bounds.height() + current_height_delta);
145 return current_frame_bounds; 186 return current_frame_bounds;
146 } 187 }
147 188
148 void OmniboxPopupContentsView::LayoutChildren() { 189 void OmniboxPopupContentsView::LayoutChildren() {
149 gfx::Rect contents_rect = GetContentsBounds(); 190 gfx::Rect contents_rect = GetContentsBounds();
150 contents_rect.Inset(GetLayoutInsets(OMNIBOX_DROPDOWN)); 191 contents_rect.Inset(GetLayoutInsets(OMNIBOX_DROPDOWN));
151 contents_rect.Inset(start_margin_, g_top_shadow.Get().height(), end_margin_, 192 contents_rect.Inset(start_margin_, g_top_shadow_height, end_margin_, 0);
152 0);
153 193
154 int top = contents_rect.y(); 194 int top = contents_rect.y();
155 for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) { 195 for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) {
156 View* v = child_at(i); 196 View* v = child_at(i);
157 if (v->visible()) { 197 if (v->visible()) {
158 v->SetBounds(contents_rect.x(), top, contents_rect.width(), 198 v->SetBounds(contents_rect.x(), top, contents_rect.width(),
159 v->GetPreferredSize().height()); 199 v->GetPreferredSize().height());
160 top = v->bounds().bottom(); 200 top = v->bounds().bottom();
161 } 201 }
162 } 202 }
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 max_match_contents_width_, view->GetMatchContentsWidth()); 259 max_match_contents_width_, view->GetMatchContentsWidth());
220 } 260 }
221 } 261 }
222 262
223 for (size_t i = result_size; i < AutocompleteResult::kMaxMatches; ++i) 263 for (size_t i = result_size; i < AutocompleteResult::kMaxMatches; ++i)
224 child_at(i)->SetVisible(false); 264 child_at(i)->SetVisible(false);
225 265
226 // We want the popup to appear to overlay the bottom of the toolbar. So we 266 // We want the popup to appear to overlay the bottom of the toolbar. So we
227 // shift the popup to completely cover the client edge, and then draw an 267 // shift the popup to completely cover the client edge, and then draw an
228 // additional semitransparent shadow above that. 268 // additional semitransparent shadow above that.
229 int top_edge_overlap = views::NonClientFrameView::kClientEdgeThickness + 269 int top_edge_overlap =
230 g_top_shadow.Get().height(); 270 views::NonClientFrameView::kClientEdgeThickness + g_top_shadow_height;
231 271
232 gfx::Point top_left_screen_coord; 272 gfx::Point top_left_screen_coord;
233 int width; 273 int width;
234 location_bar_view_->GetOmniboxPopupPositioningInfo( 274 location_bar_view_->GetOmniboxPopupPositioningInfo(
235 &top_left_screen_coord, &width, &start_margin_, 275 &top_left_screen_coord, &width, &start_margin_,
236 &end_margin_, top_edge_overlap); 276 &end_margin_, top_edge_overlap);
237 gfx::Rect new_target_bounds(top_left_screen_coord, 277 gfx::Rect new_target_bounds(top_left_screen_coord,
238 gfx::Size(width, CalculatePopupHeight())); 278 gfx::Size(width, CalculatePopupHeight()));
239 279
240 // If we're animating and our target height changes, reset the animation. 280 // If we're animating and our target height changes, reset the animation.
241 // NOTE: If we just reset blindly on _every_ update, then when the user types 281 // NOTE: If we just reset blindly on _every_ update, then when the user types
242 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the 282 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the
243 // last few pixels to get to one visible result. 283 // last few pixels to get to one visible result.
244 if (new_target_bounds.height() != target_bounds_.height()) 284 if (new_target_bounds.height() != target_bounds_.height())
245 size_animation_.Reset(); 285 size_animation_.Reset();
246 target_bounds_ = new_target_bounds; 286 target_bounds_ = new_target_bounds;
247 287
248 if (!popup_) { 288 if (!popup_) {
289 // Create a new layer even if one already exists to make sure it's correctly
290 // parented.
Evan Stade 2016/12/01 02:39:02 I'm not entirely sure why this is necessary --- it
sadrul 2016/12/06 20:05:22 Yeah ... this seems weird.
291 SetPaintToLayer(false);
292 SetPaintToLayer(true);
293 layer()->SetFillsBoundsOpaquely(false);
294 layer()->Add(shadow_layer_.get());
295
249 views::Widget* popup_parent = location_bar_view_->GetWidget(); 296 views::Widget* popup_parent = location_bar_view_->GetWidget();
250 297
251 // If the popup is currently closed, we need to create it. 298 // If the popup is currently closed, we need to create it.
252 popup_ = (new AutocompletePopupWidget(popup_parent))->AsWeakPtr(); 299 popup_ = (new AutocompletePopupWidget(popup_parent))->AsWeakPtr();
253 300
254 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 301 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
255 #if defined(OS_WIN) 302 #if defined(OS_WIN)
256 // On Windows use the software compositor to ensure that we don't block 303 // On Windows use the software compositor to ensure that we don't block
257 // the UI thread blocking issue during command buffer creation. We can 304 // the UI thread blocking issue during command buffer creation. We can
258 // revert this change once http://crbug.com/125248 is fixed. 305 // revert this change once http://crbug.com/125248 is fixed.
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 // We need to manually schedule a paint here since we are a layered window and 397 // We need to manually schedule a paint here since we are a layered window and
351 // won't implicitly require painting until we ask for one. 398 // won't implicitly require painting until we ask for one.
352 SchedulePaint(); 399 SchedulePaint();
353 } 400 }
354 401
355 views::View* OmniboxPopupContentsView::GetTooltipHandlerForPoint( 402 views::View* OmniboxPopupContentsView::GetTooltipHandlerForPoint(
356 const gfx::Point& point) { 403 const gfx::Point& point) {
357 return nullptr; 404 return nullptr;
358 } 405 }
359 406
407 void OmniboxPopupContentsView::OnBoundsChanged(
408 const gfx::Rect& previous_bounds) {
409 gfx::Rect shadow_bounds = GetLocalBounds();
410 shadow_layer_->SetBounds(shadow_bounds);
411 shadow_bounds.Inset(
412 gfx::Insets(g_top_shadow_height, 0, g_bottom_shadow_height, 0));
413 shadow_layer_->UpdateNinePatchOcclusion(shadow_bounds);
sadrul 2016/12/06 20:05:22 ui::Shadow seems to update layer-border/aperture a
414 }
415
360 bool OmniboxPopupContentsView::OnMousePressed( 416 bool OmniboxPopupContentsView::OnMousePressed(
361 const ui::MouseEvent& event) { 417 const ui::MouseEvent& event) {
362 ignore_mouse_drag_ = false; // See comment on |ignore_mouse_drag_| in header. 418 ignore_mouse_drag_ = false; // See comment on |ignore_mouse_drag_| in header.
363 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) 419 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton())
364 UpdateLineEvent(event, event.IsLeftMouseButton()); 420 UpdateLineEvent(event, event.IsLeftMouseButton());
365 return true; 421 return true;
366 } 422 }
367 423
368 bool OmniboxPopupContentsView::OnMouseDragged( 424 bool OmniboxPopupContentsView::OnMouseDragged(
369 const ui::MouseEvent& event) { 425 const ui::MouseEvent& event) {
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 int OmniboxPopupContentsView::CalculatePopupHeight() { 484 int OmniboxPopupContentsView::CalculatePopupHeight() {
429 DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size()); 485 DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size());
430 int popup_height = 0; 486 int popup_height = 0;
431 for (size_t i = 0; i < model_->result().size(); ++i) 487 for (size_t i = 0; i < model_->result().size(); ++i)
432 popup_height += child_at(i)->GetPreferredSize().height(); 488 popup_height += child_at(i)->GetPreferredSize().height();
433 489
434 // Add enough space on the top and bottom so it looks like there is the same 490 // Add enough space on the top and bottom so it looks like there is the same
435 // amount of space between the text and the popup border as there is in the 491 // amount of space between the text and the popup border as there is in the
436 // interior between each row of text. 492 // interior between each row of text.
437 return popup_height + GetLayoutInsets(OMNIBOX_DROPDOWN).height() + 493 return popup_height + GetLayoutInsets(OMNIBOX_DROPDOWN).height() +
438 g_top_shadow.Get().height() + g_bottom_shadow.Get().height(); 494 g_top_shadow_height + g_bottom_shadow_height;
439 } 495 }
440 496
441 OmniboxResultView* OmniboxPopupContentsView::CreateResultView( 497 OmniboxResultView* OmniboxPopupContentsView::CreateResultView(
442 int model_index, 498 int model_index,
443 const gfx::FontList& font_list) { 499 const gfx::FontList& font_list) {
444 return new OmniboxResultView(this, model_index, font_list); 500 return new OmniboxResultView(this, model_index, font_list);
445 } 501 }
446 502
447 //////////////////////////////////////////////////////////////////////////////// 503 ////////////////////////////////////////////////////////////////////////////////
448 // OmniboxPopupContentsView, views::View overrides, private: 504 // OmniboxPopupContentsView, views::View overrides, private:
449 505
450 const char* OmniboxPopupContentsView::GetClassName() const { 506 const char* OmniboxPopupContentsView::GetClassName() const {
451 return "OmniboxPopupContentsView"; 507 return "OmniboxPopupContentsView";
452 } 508 }
453 509
454 void OmniboxPopupContentsView::OnPaint(gfx::Canvas* canvas) {
455 canvas->TileImageInt(g_top_shadow.Get(), 0, 0, width(),
456 g_top_shadow.Get().height());
457 canvas->TileImageInt(g_bottom_shadow.Get(), 0,
458 height() - g_bottom_shadow.Get().height(), width(),
459 g_bottom_shadow.Get().height());
460 }
461
462 void OmniboxPopupContentsView::PaintChildren(const ui::PaintContext& context) { 510 void OmniboxPopupContentsView::PaintChildren(const ui::PaintContext& context) {
463 gfx::Rect contents_bounds = GetContentsBounds(); 511 gfx::Rect contents_bounds = GetContentsBounds();
464 contents_bounds.Inset(0, g_top_shadow.Get().height(), 0, 512 contents_bounds.Inset(
465 g_bottom_shadow.Get().height()); 513 gfx::Insets(g_top_shadow_height, 0, g_bottom_shadow_height, 0));
466 514
467 ui::ClipRecorder clip_recorder(context); 515 ui::ClipRecorder clip_recorder(context);
468 clip_recorder.ClipRect(contents_bounds); 516 clip_recorder.ClipRect(contents_bounds);
469 { 517 {
470 ui::PaintRecorder recorder(context, size()); 518 ui::PaintRecorder recorder(context, size());
471 SkColor background_color = result_view_at(0)->GetColor( 519 SkColor background_color = result_view_at(0)->GetColor(
472 OmniboxResultView::NORMAL, OmniboxResultView::BACKGROUND); 520 OmniboxResultView::NORMAL, OmniboxResultView::BACKGROUND);
473 recorder.canvas()->DrawColor(background_color); 521 recorder.canvas()->DrawColor(background_color);
474 } 522 }
475 View::PaintChildren(context); 523 View::PaintChildren(context);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 size_t index = GetIndexForPoint(event.location()); 573 size_t index = GetIndexForPoint(event.location());
526 if (!HasMatchAt(index)) 574 if (!HasMatchAt(index))
527 return; 575 return;
528 omnibox_view_->OpenMatch(model_->result().match_at(index), disposition, 576 omnibox_view_->OpenMatch(model_->result().match_at(index), disposition,
529 GURL(), base::string16(), index); 577 GURL(), base::string16(), index);
530 } 578 }
531 579
532 OmniboxResultView* OmniboxPopupContentsView::result_view_at(size_t i) { 580 OmniboxResultView* OmniboxPopupContentsView::result_view_at(size_t i) {
533 return static_cast<OmniboxResultView*>(child_at(static_cast<int>(i))); 581 return static_cast<OmniboxResultView*>(child_at(static_cast<int>(i)));
534 } 582 }
OLDNEW
« cc/layers/nine_patch_layer.h ('K') | « chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698