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 "ash/frame/default_header_painter.h"
7 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
8 #include "ash/frame/header_painter_util.h"
9 #include "base/debug/leak_annotations.h"
10 #include "base/logging.h" // DCHECK
11 #include "grit/ash_resources.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "third_party/skia/include/core/SkPaint.h"
14 #include "third_party/skia/include/core/SkPath.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/gfx/animation/slide_animation.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/font_list.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/gfx/rect.h"
21 #include "ui/gfx/skia_util.h"
22 #include "ui/views/view.h"
23 #include "ui/views/widget/native_widget_aura.h"
24 #include "ui/views/widget/widget.h"
25 #include "ui/views/widget/widget_delegate.h"
31 // Color for the window title text.
32 const SkColor kTitleTextColor
= SkColorSetRGB(40, 40, 40);
33 // Color of the active window header/content separator line.
34 const SkColor kHeaderContentSeparatorColor
= SkColorSetRGB(150, 150, 152);
35 // Color of the inactive window header/content separator line.
36 const SkColor kHeaderContentSeparatorInactiveColor
=
37 SkColorSetRGB(180, 180, 182);
38 // Duration of crossfade animation for activating and deactivating frame.
39 const int kActivationCrossfadeDurationMs
= 200;
41 // Tiles an image into an area, rounding the top corners.
42 void TileRoundRect(gfx::Canvas
* canvas
,
43 const gfx::ImageSkia
& image
,
45 const gfx::Rect
& bounds
,
47 SkRect rect
= gfx::RectToSkRect(bounds
);
48 const SkScalar corner_radius_scalar
= SkIntToScalar(corner_radius
);
50 corner_radius_scalar
, corner_radius_scalar
, // top-left
51 corner_radius_scalar
, corner_radius_scalar
, // top-right
55 path
.addRoundRect(rect
, radii
, SkPath::kCW_Direction
);
56 canvas
->DrawImageInPath(image
, 0, 0, path
, paint
);
59 // Returns the FontList to use for the title.
60 const gfx::FontList
& GetTitleFontList() {
61 static const gfx::FontList
* title_font_list
=
62 new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList());
63 ANNOTATE_LEAKING_OBJECT_PTR(title_font_list
);
64 return *title_font_list
;
71 ///////////////////////////////////////////////////////////////////////////////
72 // DefaultHeaderPainter, public:
74 DefaultHeaderPainter::DefaultHeaderPainter()
78 window_icon_size_(HeaderPainterUtil::GetDefaultIconSize()),
79 caption_button_container_(NULL
),
83 activation_animation_(new gfx::SlideAnimation(this)) {
86 DefaultHeaderPainter::~DefaultHeaderPainter() {
89 void DefaultHeaderPainter::Init(
91 views::View
* header_view
,
92 views::View
* window_icon
,
93 FrameCaptionButtonContainerView
* caption_button_container
) {
96 // window_icon may be NULL.
97 DCHECK(caption_button_container
);
100 window_icon_
= window_icon
;
101 caption_button_container_
= caption_button_container
;
103 caption_button_container_
->SetButtonImages(
104 CAPTION_BUTTON_ICON_MINIMIZE
,
105 IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE
,
106 IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_I
,
107 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
108 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
109 caption_button_container_
->SetButtonImages(
110 CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE
,
111 IDR_AURA_WINDOW_CONTROL_ICON_SIZE
,
112 IDR_AURA_WINDOW_CONTROL_ICON_SIZE_I
,
113 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
114 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
115 caption_button_container_
->SetButtonImages(
116 CAPTION_BUTTON_ICON_CLOSE
,
117 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
118 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I
,
119 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
120 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
122 // There is no dedicated icon for the snap-left and snap-right buttons
123 // when |frame_| is inactive because they should never be visible while
124 // |frame_| is inactive.
125 caption_button_container_
->SetButtonImages(
126 CAPTION_BUTTON_ICON_LEFT_SNAPPED
,
127 IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
128 IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
129 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
130 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
131 caption_button_container_
->SetButtonImages(
132 CAPTION_BUTTON_ICON_RIGHT_SNAPPED
,
133 IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
134 IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
135 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
136 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
139 int DefaultHeaderPainter::GetMinimumHeaderWidth() const {
140 // Ensure we have enough space for the window icon and buttons. We allow
141 // the title string to collapse to zero width.
142 return GetTitleBounds().x() +
143 caption_button_container_
->GetMinimumSize().width();
146 void DefaultHeaderPainter::PaintHeader(gfx::Canvas
* canvas
, Mode mode
) {
147 Mode old_mode
= mode_
;
150 if (mode_
!= old_mode
) {
151 if (!initial_paint_
&& HeaderPainterUtil::CanAnimateActivation(frame_
)) {
152 activation_animation_
->SetSlideDuration(kActivationCrossfadeDurationMs
);
153 if (mode_
== MODE_ACTIVE
)
154 activation_animation_
->Show();
156 activation_animation_
->Hide();
158 if (mode_
== MODE_ACTIVE
)
159 activation_animation_
->Reset(1);
161 activation_animation_
->Reset(0);
163 initial_paint_
= false;
166 int corner_radius
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
167 0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
169 int active_alpha
= activation_animation_
->CurrentValueBetween(0, 255);
170 int inactive_alpha
= 255 - active_alpha
;
173 if (inactive_alpha
> 0) {
174 if (active_alpha
> 0)
175 paint
.setXfermodeMode(SkXfermode::kPlus_Mode
);
177 paint
.setAlpha(inactive_alpha
);
178 gfx::ImageSkia inactive_frame
= *GetInactiveFrameImage();
179 TileRoundRect(canvas
, inactive_frame
, paint
, GetLocalBounds(),
183 if (active_alpha
> 0) {
184 paint
.setAlpha(active_alpha
);
185 gfx::ImageSkia active_frame
= *GetActiveFrameImage();
186 TileRoundRect(canvas
, active_frame
, paint
, GetLocalBounds(),
190 if (!frame_
->IsMaximized() &&
191 !frame_
->IsFullscreen() &&
192 mode_
== MODE_INACTIVE
) {
193 PaintHighlightForInactiveRestoredWindow(canvas
);
195 if (frame_
->widget_delegate() &&
196 frame_
->widget_delegate()->ShouldShowWindowTitle()) {
197 PaintTitleBar(canvas
);
199 PaintHeaderContentSeparator(canvas
);
202 void DefaultHeaderPainter::LayoutHeader() {
203 caption_button_container_
->Layout();
205 gfx::Size caption_button_container_size
=
206 caption_button_container_
->GetPreferredSize();
207 caption_button_container_
->SetBounds(
208 view_
->width() - caption_button_container_size
.width(),
210 caption_button_container_size
.width(),
211 caption_button_container_size
.height());
214 // Vertically center the window icon with respect to the caption button
216 // Floor when computing the center of |caption_button_container_|.
218 caption_button_container_
->height() / 2 - window_icon_size_
/ 2;
219 window_icon_
->SetBounds(HeaderPainterUtil::GetIconXOffset(), icon_offset_y
,
220 window_icon_size_
, window_icon_size_
);
223 // The header/content separator line overlays the caption buttons.
224 SetHeaderHeightForPainting(caption_button_container_
->height());
227 int DefaultHeaderPainter::GetHeaderHeightForPainting() const {
231 void DefaultHeaderPainter::SetHeaderHeightForPainting(int height
) {
235 void DefaultHeaderPainter::SchedulePaintForTitle() {
236 view_
->SchedulePaintInRect(GetTitleBounds());
239 void DefaultHeaderPainter::UpdateWindowIcon(views::View
* window_icon
,
240 int window_icon_size
) {
241 window_icon_
= window_icon
;
242 window_icon_size_
= window_icon_size
;
245 ///////////////////////////////////////////////////////////////////////////////
246 // gfx::AnimationDelegate overrides:
248 void DefaultHeaderPainter::AnimationProgressed(
249 const gfx::Animation
* animation
) {
250 view_
->SchedulePaintInRect(GetLocalBounds());
253 ///////////////////////////////////////////////////////////////////////////////
254 // DefaultHeaderPainter, private:
256 void DefaultHeaderPainter::PaintHighlightForInactiveRestoredWindow(
257 gfx::Canvas
* canvas
) {
258 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
259 gfx::ImageSkia top_edge
= *rb
.GetImageSkiaNamed(
260 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP
);
261 gfx::ImageSkia left_edge
= *rb
.GetImageSkiaNamed(
262 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT
);
263 gfx::ImageSkia right_edge
= *rb
.GetImageSkiaNamed(
264 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT
);
265 gfx::ImageSkia bottom_edge
= *rb
.GetImageSkiaNamed(
266 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM
);
268 int left_edge_width
= left_edge
.width();
269 int right_edge_width
= right_edge
.width();
270 canvas
->DrawImageInt(left_edge
, 0, 0);
271 canvas
->DrawImageInt(right_edge
, view_
->width() - right_edge_width
, 0);
272 canvas
->TileImageInt(
276 view_
->width() - left_edge_width
- right_edge_width
,
279 DCHECK_EQ(left_edge
.height(), right_edge
.height());
280 int bottom
= left_edge
.height();
281 int bottom_height
= bottom_edge
.height();
282 canvas
->TileImageInt(
285 bottom
- bottom_height
,
286 view_
->width() - left_edge_width
- right_edge_width
,
290 void DefaultHeaderPainter::PaintTitleBar(gfx::Canvas
* canvas
) {
291 // The window icon is painted by its own views::View.
292 gfx::Rect title_bounds
= GetTitleBounds();
293 title_bounds
.set_x(view_
->GetMirroredXForRect(title_bounds
));
294 canvas
->DrawStringRectWithFlags(frame_
->widget_delegate()->GetWindowTitle(),
298 gfx::Canvas::NO_SUBPIXEL_RENDERING
);
301 void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas
* canvas
) {
302 SkColor color
= (mode_
== MODE_ACTIVE
) ?
303 kHeaderContentSeparatorColor
:
304 kHeaderContentSeparatorInactiveColor
;
307 paint
.setColor(color
);
308 // Draw the line as 1px thick regardless of scale factor.
309 paint
.setStrokeWidth(0);
311 float thickness
= 1 / canvas
->image_scale();
312 SkScalar y
= SkIntToScalar(height_
) - SkFloatToScalar(thickness
);
313 canvas
->sk_canvas()->drawLine(0, y
, SkIntToScalar(view_
->width()), y
, paint
);
316 gfx::Rect
DefaultHeaderPainter::GetLocalBounds() const {
317 return gfx::Rect(view_
->width(), height_
);
320 gfx::Rect
DefaultHeaderPainter::GetTitleBounds() const {
321 return HeaderPainterUtil::GetTitleBounds(
322 window_icon_
, caption_button_container_
, GetTitleFontList());
325 gfx::ImageSkia
* DefaultHeaderPainter::GetActiveFrameImage() const {
326 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
327 IDR_AURA_WINDOW_HEADER_BASE
);
330 gfx::ImageSkia
* DefaultHeaderPainter::GetInactiveFrameImage() const {
331 int frame_image_id
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
332 IDR_AURA_WINDOW_HEADER_BASE
:
333 IDR_AURA_WINDOW_HEADER_BASE_RESTORED_INACTIVE
;
334 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(