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/image/image_skia.h"
23 #include "ui/gfx/rect.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"
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
,
42 const gfx::Rect
& bounds
,
43 int top_left_corner_radius
,
44 int top_right_corner_radius
,
46 SkRect rect
= gfx::RectToSkRect(bounds
);
47 const SkScalar kTopLeftRadius
= SkIntToScalar(top_left_corner_radius
);
48 const SkScalar kTopRightRadius
= SkIntToScalar(top_right_corner_radius
);
50 kTopLeftRadius
, kTopLeftRadius
, // top-left
51 kTopRightRadius
, kTopRightRadius
, // top-right
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
61 void PaintFrameImagesInRoundRect(gfx::Canvas
* canvas
,
62 const gfx::ImageSkia
& frame_image
,
63 const gfx::ImageSkia
& frame_overlay_image
,
65 const gfx::Rect
& bounds
,
68 SkXfermode::Mode normal_mode
;
69 SkXfermode::AsMode(NULL
, &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
));
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);
95 gfx::Canvas
temporary_canvas(bounds
.size(), canvas
->image_scale(), false);
96 temporary_canvas
.TileImageInt(frame_image
,
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);
108 ///////////////////////////////////////////////////////////////////////////////
109 // BrowserHeaderPainterAsh, public:
111 BrowserHeaderPainterAsh::BrowserHeaderPainterAsh()
114 is_incognito_(false),
117 caption_button_container_(NULL
),
119 initial_paint_(true),
120 mode_(MODE_INACTIVE
),
121 activation_animation_(new gfx::SlideAnimation(this)) {
124 BrowserHeaderPainterAsh::~BrowserHeaderPainterAsh() {
127 void BrowserHeaderPainterAsh::Init(
128 views::Widget
* frame
,
129 BrowserView
* browser_view
,
130 views::View
* header_view
,
131 views::View
* window_icon
,
132 ash::FrameCaptionButtonContainerView
* caption_button_container
) {
134 DCHECK(browser_view
);
136 // window_icon may be NULL.
137 DCHECK(caption_button_container
);
140 is_tabbed_
= browser_view
->browser()->is_type_tabbed();
141 is_incognito_
= !browser_view
->IsRegularOrGuestSession();
144 window_icon_
= window_icon
;
145 caption_button_container_
= caption_button_container
;
148 int BrowserHeaderPainterAsh::GetMinimumHeaderWidth() const {
149 // Ensure we have enough space for the window icon and buttons. We allow
150 // the title string to collapse to zero width.
151 return GetTitleBounds().x() +
152 caption_button_container_
->GetMinimumSize().width();
155 void BrowserHeaderPainterAsh::PaintHeader(gfx::Canvas
* canvas
, Mode mode
) {
156 Mode old_mode
= mode_
;
159 if (mode_
!= old_mode
) {
160 if (!initial_paint_
&&
161 ash::HeaderPainterUtil::CanAnimateActivation(frame_
)) {
162 activation_animation_
->SetSlideDuration(kActivationCrossfadeDurationMs
);
163 if (mode_
== MODE_ACTIVE
)
164 activation_animation_
->Show();
166 activation_animation_
->Hide();
168 if (mode_
== MODE_ACTIVE
)
169 activation_animation_
->Reset(1);
171 activation_animation_
->Reset(0);
173 initial_paint_
= false;
176 int corner_radius
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
177 0 : ash::HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
179 int active_alpha
= activation_animation_
->CurrentValueBetween(0, 255);
180 int inactive_alpha
= 255 - active_alpha
;
183 if (inactive_alpha
> 0) {
184 if (active_alpha
> 0)
185 paint
.setXfermodeMode(SkXfermode::kPlus_Mode
);
187 gfx::ImageSkia inactive_frame_image
;
188 gfx::ImageSkia inactive_frame_overlay_image
;
189 GetFrameImages(MODE_INACTIVE
, &inactive_frame_image
,
190 &inactive_frame_overlay_image
);
192 paint
.setAlpha(inactive_alpha
);
193 PaintFrameImagesInRoundRect(
195 inactive_frame_image
,
196 inactive_frame_overlay_image
,
200 ash::HeaderPainterUtil::GetThemeBackgroundXInset());
203 if (active_alpha
> 0) {
204 gfx::ImageSkia active_frame_image
;
205 gfx::ImageSkia active_frame_overlay_image
;
206 GetFrameImages(MODE_ACTIVE
, &active_frame_image
,
207 &active_frame_overlay_image
);
209 paint
.setAlpha(active_alpha
);
210 PaintFrameImagesInRoundRect(
213 active_frame_overlay_image
,
217 ash::HeaderPainterUtil::GetThemeBackgroundXInset());
220 if (!frame_
->IsMaximized() && !frame_
->IsFullscreen())
221 PaintHighlightForRestoredWindow(canvas
);
222 if (frame_
->widget_delegate() &&
223 frame_
->widget_delegate()->ShouldShowWindowTitle()) {
224 PaintTitleBar(canvas
);
228 void BrowserHeaderPainterAsh::LayoutHeader() {
229 // Purposefully set |painted_height_| to an invalid value. We cannot use
230 // |painted_height_| because the computation of |painted_height_| may depend
231 // on having laid out the window controls.
232 painted_height_
= -1;
234 UpdateCaptionButtonImages();
235 caption_button_container_
->Layout();
237 gfx::Size caption_button_container_size
=
238 caption_button_container_
->GetPreferredSize();
239 caption_button_container_
->SetBounds(
240 view_
->width() - caption_button_container_size
.width(),
242 caption_button_container_size
.width(),
243 caption_button_container_size
.height());
246 // Vertically center the window icon with respect to the caption button
248 int icon_size
= ash::HeaderPainterUtil::GetDefaultIconSize();
249 int icon_offset_y
= (caption_button_container_
->height() - icon_size
) / 2;
250 window_icon_
->SetBounds(ash::HeaderPainterUtil::GetIconXOffset(),
251 icon_offset_y
, icon_size
, icon_size
);
255 int BrowserHeaderPainterAsh::GetHeaderHeightForPainting() const {
256 return painted_height_
;
259 void BrowserHeaderPainterAsh::SetHeaderHeightForPainting(int height
) {
260 painted_height_
= height
;
263 void BrowserHeaderPainterAsh::SchedulePaintForTitle() {
264 view_
->SchedulePaintInRect(GetTitleBounds());
267 ///////////////////////////////////////////////////////////////////////////////
268 // gfx::AnimationDelegate overrides:
270 void BrowserHeaderPainterAsh::AnimationProgressed(
271 const gfx::Animation
* animation
) {
272 view_
->SchedulePaintInRect(GetPaintedBounds());
275 ///////////////////////////////////////////////////////////////////////////////
276 // BrowserHeaderPainterAsh, private:
278 void BrowserHeaderPainterAsh::PaintHighlightForRestoredWindow(
279 gfx::Canvas
* canvas
) {
280 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
281 gfx::ImageSkia top_left_corner
= *rb
.GetImageSkiaNamed(
282 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP_LEFT
);
283 gfx::ImageSkia top_right_corner
= *rb
.GetImageSkiaNamed(
284 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP_RIGHT
);
285 gfx::ImageSkia top_edge
= *rb
.GetImageSkiaNamed(
286 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_TOP
);
287 gfx::ImageSkia left_edge
= *rb
.GetImageSkiaNamed(
288 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_LEFT
);
289 gfx::ImageSkia right_edge
= *rb
.GetImageSkiaNamed(
290 IDR_ASH_BROWSER_WINDOW_HEADER_SHADE_RIGHT
);
292 int top_left_width
= top_left_corner
.width();
293 int top_left_height
= top_left_corner
.height();
294 canvas
->DrawImageInt(top_left_corner
, 0, 0);
296 int top_right_width
= top_right_corner
.width();
297 int top_right_height
= top_right_corner
.height();
298 canvas
->DrawImageInt(top_right_corner
,
299 view_
->width() - top_right_width
,
302 canvas
->TileImageInt(
306 view_
->width() - top_left_width
- top_right_width
,
309 canvas
->TileImageInt(left_edge
,
313 painted_height_
- top_left_height
);
315 canvas
->TileImageInt(right_edge
,
316 view_
->width() - right_edge
.width(),
319 painted_height_
- top_right_height
);
322 void BrowserHeaderPainterAsh::PaintTitleBar(gfx::Canvas
* canvas
) {
323 // The window icon is painted by its own views::View.
324 gfx::Rect title_bounds
= GetTitleBounds();
325 title_bounds
.set_x(view_
->GetMirroredXForRect(title_bounds
));
326 canvas
->DrawStringRectWithFlags(frame_
->widget_delegate()->GetWindowTitle(),
327 BrowserFrame::GetTitleFontList(),
328 kWindowTitleTextColor
,
330 gfx::Canvas::NO_SUBPIXEL_RENDERING
);
333 void BrowserHeaderPainterAsh::GetFrameImages(
335 gfx::ImageSkia
* frame_image
,
336 gfx::ImageSkia
* frame_overlay_image
) const {
338 GetFrameImagesForTabbedBrowser(mode
, frame_image
, frame_overlay_image
);
340 *frame_image
= GetFrameImageForNonTabbedBrowser(mode
);
341 *frame_overlay_image
= gfx::ImageSkia();
345 void BrowserHeaderPainterAsh::GetFrameImagesForTabbedBrowser(
347 gfx::ImageSkia
* frame_image
,
348 gfx::ImageSkia
* frame_overlay_image
) const {
349 int frame_image_id
= 0;
350 int frame_overlay_image_id
= 0;
352 ui::ThemeProvider
* tp
= frame_
->GetThemeProvider();
353 if (tp
->HasCustomImage(IDR_THEME_FRAME_OVERLAY
) && !is_incognito_
) {
354 frame_overlay_image_id
= (mode
== MODE_ACTIVE
) ?
355 IDR_THEME_FRAME_OVERLAY
: IDR_THEME_FRAME_OVERLAY_INACTIVE
;
358 if (mode
== MODE_ACTIVE
) {
359 frame_image_id
= is_incognito_
?
360 IDR_THEME_FRAME_INCOGNITO
: IDR_THEME_FRAME
;
362 frame_image_id
= is_incognito_
?
363 IDR_THEME_FRAME_INCOGNITO_INACTIVE
: IDR_THEME_FRAME_INACTIVE
;
366 *frame_image
= *tp
->GetImageSkiaNamed(frame_image_id
);
367 *frame_overlay_image
= (frame_overlay_image_id
== 0) ?
368 gfx::ImageSkia() : *tp
->GetImageSkiaNamed(frame_overlay_image_id
);
371 gfx::ImageSkia
BrowserHeaderPainterAsh::GetFrameImageForNonTabbedBrowser(
373 // Request the images from the ResourceBundle (and not from the ThemeProvider)
374 // in order to get the default non-themed assets.
375 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
376 if (mode
== MODE_ACTIVE
) {
377 return *rb
.GetImageSkiaNamed(is_incognito_
?
378 IDR_THEME_FRAME_INCOGNITO
: IDR_THEME_FRAME
);
380 return *rb
.GetImageSkiaNamed(is_incognito_
?
381 IDR_THEME_FRAME_INCOGNITO_INACTIVE
: IDR_THEME_FRAME_INACTIVE
);
384 void BrowserHeaderPainterAsh::UpdateCaptionButtonImages() {
385 int hover_background_id
= 0;
386 int pressed_background_id
= 0;
387 if (frame_
->IsMaximized() || frame_
->IsFullscreen()) {
388 hover_background_id
=
389 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_MAXIMIZED_H
;
390 pressed_background_id
=
391 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_MAXIMIZED_P
;
393 hover_background_id
=
394 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_RESTORED_H
;
395 pressed_background_id
=
396 IDR_ASH_BROWSER_WINDOW_CONTROL_BACKGROUND_RESTORED_P
;
398 caption_button_container_
->SetButtonImages(
399 ash::CAPTION_BUTTON_ICON_MINIMIZE
,
400 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MINIMIZE
,
401 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MINIMIZE
,
403 pressed_background_id
);
404 caption_button_container_
->SetButtonImages(
405 ash::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE
,
406 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_SIZE
,
407 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_SIZE
,
409 pressed_background_id
);
410 caption_button_container_
->SetButtonImages(
411 ash::CAPTION_BUTTON_ICON_CLOSE
,
412 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_CLOSE
,
413 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_CLOSE
,
415 pressed_background_id
);
416 caption_button_container_
->SetButtonImages(
417 ash::CAPTION_BUTTON_ICON_LEFT_SNAPPED
,
418 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
419 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
421 pressed_background_id
);
422 caption_button_container_
->SetButtonImages(
423 ash::CAPTION_BUTTON_ICON_RIGHT_SNAPPED
,
424 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
425 IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
427 pressed_background_id
);
430 gfx::Rect
BrowserHeaderPainterAsh::GetPaintedBounds() const {
431 return gfx::Rect(view_
->width(), painted_height_
);
434 gfx::Rect
BrowserHeaderPainterAsh::GetTitleBounds() const {
435 return ash::HeaderPainterUtil::GetTitleBounds(window_icon_
,
436 caption_button_container_
, BrowserFrame::GetTitleFontList());