Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / browser_header_painter_ash.cc
blob7cbe56caf7e377dd3ab86b97691f2b9bbce8863d
1 // Copyright 2014 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.
5 #include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
7 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
8 #include "ash/frame/header_painter_util.h"
9 #include "base/logging.h" // DCHECK
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/views/frame/browser_frame.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "grit/theme_resources.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/core/SkColor.h"
16 #include "third_party/skia/include/core/SkPaint.h"
17 #include "third_party/skia/include/core/SkPath.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/base/theme_provider.h"
20 #include "ui/gfx/animation/slide_animation.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/image/image_skia.h"
24 #include "ui/gfx/skia_util.h"
25 #include "ui/views/view.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/widget/widget_delegate.h"
29 using views::Widget;
31 namespace {
32 // Color for the window title text.
33 const SkColor kWindowTitleTextColor = SkColorSetRGB(40, 40, 40);
34 // Duration of crossfade animation for activating and deactivating frame.
35 const int kActivationCrossfadeDurationMs = 200;
37 // Tiles an image into an area, rounding the top corners. Samples |image|
38 // starting |image_inset_x| pixels from the left of the image.
39 void TileRoundRect(gfx::Canvas* canvas,
40 const gfx::ImageSkia& image,
41 const SkPaint& paint,
42 const gfx::Rect& bounds,
43 int top_left_corner_radius,
44 int top_right_corner_radius,
45 int image_inset_x) {
46 SkRect rect = gfx::RectToSkRect(bounds);
47 const SkScalar kTopLeftRadius = SkIntToScalar(top_left_corner_radius);
48 const SkScalar kTopRightRadius = SkIntToScalar(top_right_corner_radius);
49 SkScalar radii[8] = {
50 kTopLeftRadius, kTopLeftRadius, // top-left
51 kTopRightRadius, kTopRightRadius, // top-right
52 0, 0, // bottom-right
53 0, 0}; // bottom-left
54 SkPath path;
55 path.addRoundRect(rect, radii, SkPath::kCW_Direction);
56 canvas->DrawImageInPath(image, -image_inset_x, 0, path, paint);
59 // Tiles |frame_image| and |frame_overlay_image| into an area, rounding the top
60 // corners.
61 void PaintFrameImagesInRoundRect(gfx::Canvas* canvas,
62 const gfx::ImageSkia& frame_image,
63 const gfx::ImageSkia& frame_overlay_image,
64 const SkPaint& paint,
65 const gfx::Rect& bounds,
66 int corner_radius,
67 int image_inset_x) {
68 SkXfermode::Mode normal_mode;
69 SkXfermode::AsMode(nullptr, &normal_mode);
71 // If |paint| is using an unusual SkXfermode::Mode (this is the case while
72 // crossfading), we must create a new canvas to overlay |frame_image| and
73 // |frame_overlay_image| using |normal_mode| and then paint the result
74 // using the unusual mode. We try to avoid this because creating a new
75 // browser-width canvas is expensive.
76 bool fast_path = (frame_overlay_image.isNull() ||
77 SkXfermode::IsMode(paint.getXfermode(), normal_mode));
78 if (fast_path) {
79 TileRoundRect(canvas, frame_image, paint, bounds, corner_radius,
80 corner_radius, image_inset_x);
82 if (!frame_overlay_image.isNull()) {
83 // Adjust |bounds| such that |frame_overlay_image| is not tiled.
84 gfx::Rect overlay_bounds = bounds;
85 overlay_bounds.Intersect(
86 gfx::Rect(bounds.origin(), frame_overlay_image.size()));
87 int top_left_corner_radius = corner_radius;
88 int top_right_corner_radius = corner_radius;
89 if (overlay_bounds.width() < bounds.width() - corner_radius)
90 top_right_corner_radius = 0;
91 TileRoundRect(canvas, frame_overlay_image, paint, overlay_bounds,
92 top_left_corner_radius, top_right_corner_radius, 0);
94 } else {
95 gfx::Canvas temporary_canvas(bounds.size(), canvas->image_scale(), false);
96 temporary_canvas.TileImageInt(frame_image,
97 image_inset_x, 0,
98 0, 0,
99 bounds.width(), bounds.height());
100 temporary_canvas.DrawImageInt(frame_overlay_image, 0, 0);
101 TileRoundRect(canvas, gfx::ImageSkia(temporary_canvas.ExtractImageRep()),
102 paint, bounds, corner_radius, corner_radius, 0);
106 } // namespace
108 ///////////////////////////////////////////////////////////////////////////////
109 // BrowserHeaderPainterAsh, public:
111 BrowserHeaderPainterAsh::BrowserHeaderPainterAsh()
112 : frame_(nullptr),
113 is_tabbed_(false),
114 is_incognito_(false),
115 view_(nullptr),
116 window_icon_(nullptr),
117 window_icon_x_inset_(ash::HeaderPainterUtil::GetDefaultLeftViewXInset()),
118 caption_button_container_(nullptr),
119 painted_height_(0),
120 initial_paint_(true),
121 mode_(MODE_INACTIVE),
122 activation_animation_(new gfx::SlideAnimation(this)) {
125 BrowserHeaderPainterAsh::~BrowserHeaderPainterAsh() {
128 void BrowserHeaderPainterAsh::Init(
129 views::Widget* frame,
130 BrowserView* browser_view,
131 views::View* header_view,
132 views::View* window_icon,
133 ash::FrameCaptionButtonContainerView* caption_button_container) {
134 DCHECK(frame);
135 DCHECK(browser_view);
136 DCHECK(header_view);
137 // window_icon may be null.
138 DCHECK(caption_button_container);
139 frame_ = frame;
141 is_tabbed_ = browser_view->browser()->is_type_tabbed();
142 is_incognito_ = !browser_view->IsRegularOrGuestSession();
144 view_ = header_view;
145 window_icon_ = window_icon;
146 caption_button_container_ = caption_button_container;
149 int BrowserHeaderPainterAsh::GetMinimumHeaderWidth() const {
150 // Ensure we have enough space for the window icon and buttons. We allow
151 // the title string to collapse to zero width.
152 return GetTitleBounds().x() +
153 caption_button_container_->GetMinimumSize().width();
156 void BrowserHeaderPainterAsh::PaintHeader(gfx::Canvas* canvas, Mode mode) {
157 Mode old_mode = mode_;
158 mode_ = mode;
160 if (mode_ != old_mode) {
161 if (!initial_paint_ &&
162 ash::HeaderPainterUtil::CanAnimateActivation(frame_)) {
163 activation_animation_->SetSlideDuration(kActivationCrossfadeDurationMs);
164 if (mode_ == MODE_ACTIVE)
165 activation_animation_->Show();
166 else
167 activation_animation_->Hide();
168 } else {
169 if (mode_ == MODE_ACTIVE)
170 activation_animation_->Reset(1);
171 else
172 activation_animation_->Reset(0);
174 initial_paint_ = false;
177 int corner_radius = (frame_->IsMaximized() || frame_->IsFullscreen()) ?
178 0 : ash::HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
180 int active_alpha = activation_animation_->CurrentValueBetween(0, 255);
181 int inactive_alpha = 255 - active_alpha;
183 SkPaint paint;
184 if (inactive_alpha > 0) {
185 if (active_alpha > 0)
186 paint.setXfermodeMode(SkXfermode::kPlus_Mode);
188 gfx::ImageSkia inactive_frame_image;
189 gfx::ImageSkia inactive_frame_overlay_image;
190 GetFrameImages(MODE_INACTIVE, &inactive_frame_image,
191 &inactive_frame_overlay_image);
193 paint.setAlpha(inactive_alpha);
194 PaintFrameImagesInRoundRect(
195 canvas,
196 inactive_frame_image,
197 inactive_frame_overlay_image,
198 paint,
199 GetPaintedBounds(),
200 corner_radius,
201 ash::HeaderPainterUtil::GetThemeBackgroundXInset());
204 if (active_alpha > 0) {
205 gfx::ImageSkia active_frame_image;
206 gfx::ImageSkia active_frame_overlay_image;
207 GetFrameImages(MODE_ACTIVE, &active_frame_image,
208 &active_frame_overlay_image);
210 paint.setAlpha(active_alpha);
211 PaintFrameImagesInRoundRect(
212 canvas,
213 active_frame_image,
214 active_frame_overlay_image,
215 paint,
216 GetPaintedBounds(),
217 corner_radius,
218 ash::HeaderPainterUtil::GetThemeBackgroundXInset());
221 if (!frame_->IsMaximized() && !frame_->IsFullscreen())
222 PaintHighlightForRestoredWindow(canvas);
223 if (frame_->widget_delegate() &&
224 frame_->widget_delegate()->ShouldShowWindowTitle()) {
225 PaintTitleBar(canvas);
229 void BrowserHeaderPainterAsh::LayoutHeader() {
230 // Purposefully set |painted_height_| to an invalid value. We cannot use
231 // |painted_height_| because the computation of |painted_height_| may depend
232 // on having laid out the window controls.
233 painted_height_ = -1;
235 UpdateCaptionButtonImages();
236 caption_button_container_->Layout();
238 gfx::Size caption_button_container_size =
239 caption_button_container_->GetPreferredSize();
240 caption_button_container_->SetBounds(
241 view_->width() - caption_button_container_size.width(),
243 caption_button_container_size.width(),
244 caption_button_container_size.height());
246 if (window_icon_) {
247 // Vertically center the window icon with respect to the caption button
248 // container.
249 gfx::Size icon_size(window_icon_->GetPreferredSize());
250 int icon_offset_y = (caption_button_container_->height() -
251 icon_size.height()) / 2;
252 window_icon_->SetBounds(window_icon_x_inset_,
253 icon_offset_y,
254 icon_size.width(),
255 icon_size.height());
259 int BrowserHeaderPainterAsh::GetHeaderHeight() const {
260 return caption_button_container_->height();
263 int BrowserHeaderPainterAsh::GetHeaderHeightForPainting() const {
264 return painted_height_;
267 void BrowserHeaderPainterAsh::SetHeaderHeightForPainting(int height) {
268 painted_height_ = height;
271 void BrowserHeaderPainterAsh::SchedulePaintForTitle() {
272 view_->SchedulePaintInRect(GetTitleBounds());
275 void BrowserHeaderPainterAsh::UpdateLeftViewXInset(int left_view_x_inset) {
276 window_icon_x_inset_ = left_view_x_inset;
279 ///////////////////////////////////////////////////////////////////////////////
280 // gfx::AnimationDelegate overrides:
282 void BrowserHeaderPainterAsh::AnimationProgressed(
283 const gfx::Animation* animation) {
284 view_->SchedulePaintInRect(GetPaintedBounds());
287 ///////////////////////////////////////////////////////////////////////////////
288 // BrowserHeaderPainterAsh, private:
290 void BrowserHeaderPainterAsh::PaintHighlightForRestoredWindow(
291 gfx::Canvas* canvas) {
292 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
293 gfx::ImageSkia top_left_corner = *rb.GetImageSkiaNamed(
294 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP_LEFT);
295 gfx::ImageSkia top_right_corner = *rb.GetImageSkiaNamed(
296 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP_RIGHT);
297 gfx::ImageSkia top_edge = *rb.GetImageSkiaNamed(
298 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP);
299 gfx::ImageSkia left_edge = *rb.GetImageSkiaNamed(
300 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_LEFT);
301 gfx::ImageSkia right_edge = *rb.GetImageSkiaNamed(
302 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_RIGHT);
304 int top_left_width = top_left_corner.width();
305 int top_left_height = top_left_corner.height();
306 canvas->DrawImageInt(top_left_corner, 0, 0);
308 int top_right_width = top_right_corner.width();
309 int top_right_height = top_right_corner.height();
310 canvas->DrawImageInt(top_right_corner,
311 view_->width() - top_right_width,
314 canvas->TileImageInt(
315 top_edge,
316 top_left_width,
318 view_->width() - top_left_width - top_right_width,
319 top_edge.height());
321 canvas->TileImageInt(left_edge,
323 top_left_height,
324 left_edge.width(),
325 painted_height_ - top_left_height);
327 canvas->TileImageInt(right_edge,
328 view_->width() - right_edge.width(),
329 top_right_height,
330 right_edge.width(),
331 painted_height_ - top_right_height);
334 void BrowserHeaderPainterAsh::PaintTitleBar(gfx::Canvas* canvas) {
335 // The window icon is painted by its own views::View.
336 gfx::Rect title_bounds = GetTitleBounds();
337 title_bounds.set_x(view_->GetMirroredXForRect(title_bounds));
338 canvas->DrawStringRectWithFlags(frame_->widget_delegate()->GetWindowTitle(),
339 BrowserFrame::GetTitleFontList(),
340 kWindowTitleTextColor,
341 title_bounds,
342 gfx::Canvas::NO_SUBPIXEL_RENDERING);
345 void BrowserHeaderPainterAsh::GetFrameImages(
346 Mode mode,
347 gfx::ImageSkia* frame_image,
348 gfx::ImageSkia* frame_overlay_image) const {
349 if (is_tabbed_) {
350 GetFrameImagesForTabbedBrowser(mode, frame_image, frame_overlay_image);
351 } else {
352 *frame_image = GetFrameImageForNonTabbedBrowser(mode);
353 *frame_overlay_image = gfx::ImageSkia();
357 void BrowserHeaderPainterAsh::GetFrameImagesForTabbedBrowser(
358 Mode mode,
359 gfx::ImageSkia* frame_image,
360 gfx::ImageSkia* frame_overlay_image) const {
361 int frame_image_id = 0;
362 int frame_overlay_image_id = 0;
364 ui::ThemeProvider* tp = frame_->GetThemeProvider();
365 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && !is_incognito_) {
366 frame_overlay_image_id = (mode == MODE_ACTIVE) ?
367 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE;
370 if (mode == MODE_ACTIVE) {
371 frame_image_id = is_incognito_ ?
372 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
373 } else {
374 frame_image_id = is_incognito_ ?
375 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
378 *frame_image = *tp->GetImageSkiaNamed(frame_image_id);
379 *frame_overlay_image = (frame_overlay_image_id == 0) ?
380 gfx::ImageSkia() : *tp->GetImageSkiaNamed(frame_overlay_image_id);
383 gfx::ImageSkia BrowserHeaderPainterAsh::GetFrameImageForNonTabbedBrowser(
384 Mode mode) const {
385 // Request the images from the ResourceBundle (and not from the ThemeProvider)
386 // in order to get the default non-themed assets.
387 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
388 if (mode == MODE_ACTIVE) {
389 return *rb.GetImageSkiaNamed(is_incognito_ ?
390 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME);
392 return *rb.GetImageSkiaNamed(is_incognito_ ?
393 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE);
396 void BrowserHeaderPainterAsh::UpdateCaptionButtonImages() {
397 int hover_background_id = 0;
398 int pressed_background_id = 0;
399 if (frame_->IsMaximized() || frame_->IsFullscreen()) {
400 hover_background_id =
401 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_MAXIMIZED_H;
402 pressed_background_id =
403 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_MAXIMIZED_P;
404 } else {
405 hover_background_id =
406 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_RESTORED_H;
407 pressed_background_id =
408 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_RESTORED_P;
410 caption_button_container_->SetButtonImages(
411 ash::CAPTION_BUTTON_ICON_MINIMIZE,
412 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MINIMIZE,
413 hover_background_id,
414 pressed_background_id);
416 int size_icon_id = 0;
417 if (frame_->IsMaximized() || frame_->IsFullscreen())
418 size_icon_id = IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RESTORE;
419 else
420 size_icon_id = IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MAXIMIZE;
421 caption_button_container_->SetButtonImages(
422 ash::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
423 size_icon_id,
424 hover_background_id,
425 pressed_background_id);
427 caption_button_container_->SetButtonImages(
428 ash::CAPTION_BUTTON_ICON_CLOSE,
429 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_CLOSE,
430 hover_background_id,
431 pressed_background_id);
432 caption_button_container_->SetButtonImages(
433 ash::CAPTION_BUTTON_ICON_LEFT_SNAPPED,
434 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
435 hover_background_id,
436 pressed_background_id);
437 caption_button_container_->SetButtonImages(
438 ash::CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
439 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
440 hover_background_id,
441 pressed_background_id);
444 gfx::Rect BrowserHeaderPainterAsh::GetPaintedBounds() const {
445 return gfx::Rect(view_->width(), painted_height_);
448 gfx::Rect BrowserHeaderPainterAsh::GetTitleBounds() const {
449 return ash::HeaderPainterUtil::GetTitleBounds(window_icon_,
450 caption_button_container_, BrowserFrame::GetTitleFontList());