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/SkPaint.h"
13 #include "third_party/skia/include/core/SkPath.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/gfx/animation/slide_animation.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/color_utils.h"
18 #include "ui/gfx/font_list.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/image/image.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 // The default color of the frame.
39 const SkColor kDefaultFrameColor
= SkColorSetRGB(242, 242, 242);
40 // Duration of crossfade animation for activating and deactivating frame.
41 const int kActivationCrossfadeDurationMs
= 200;
42 // Luminance below which to use white caption buttons.
43 const int kMaxLuminanceForLightButtons
= 125;
45 // Tiles an image into an area, rounding the top corners.
46 void TileRoundRect(gfx::Canvas
* canvas
,
48 const gfx::Rect
& bounds
,
50 SkRect rect
= gfx::RectToSkRect(bounds
);
51 const SkScalar corner_radius_scalar
= SkIntToScalar(corner_radius
);
53 corner_radius_scalar
, corner_radius_scalar
, // top-left
54 corner_radius_scalar
, corner_radius_scalar
, // top-right
58 path
.addRoundRect(rect
, radii
, SkPath::kCW_Direction
);
59 canvas
->DrawPath(path
, paint
);
62 // Returns the FontList to use for the title.
63 const gfx::FontList
& GetTitleFontList() {
64 static const gfx::FontList
* title_font_list
=
65 new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList());
66 ANNOTATE_LEAKING_OBJECT_PTR(title_font_list
);
67 return *title_font_list
;
74 ///////////////////////////////////////////////////////////////////////////////
75 // DefaultHeaderPainter, public:
77 DefaultHeaderPainter::DefaultHeaderPainter()
80 left_header_view_(NULL
),
81 left_view_x_inset_(HeaderPainterUtil::GetDefaultLeftViewXInset()),
82 active_frame_color_(kDefaultFrameColor
),
83 inactive_frame_color_(kDefaultFrameColor
),
84 caption_button_container_(NULL
),
88 activation_animation_(new gfx::SlideAnimation(this)) {}
90 DefaultHeaderPainter::~DefaultHeaderPainter() {
93 void DefaultHeaderPainter::Init(
95 views::View
* header_view
,
96 FrameCaptionButtonContainerView
* caption_button_container
) {
99 DCHECK(caption_button_container
);
102 caption_button_container_
= caption_button_container
;
103 UpdateAllButtonImages();
106 int DefaultHeaderPainter::GetMinimumHeaderWidth() const {
107 // Ensure we have enough space for the window icon and buttons. We allow
108 // the title string to collapse to zero width.
109 return GetTitleBounds().x() +
110 caption_button_container_
->GetMinimumSize().width();
113 void DefaultHeaderPainter::PaintHeader(gfx::Canvas
* canvas
, Mode mode
) {
114 Mode old_mode
= mode_
;
117 if (mode_
!= old_mode
) {
118 UpdateAllButtonImages();
119 if (!initial_paint_
&& HeaderPainterUtil::CanAnimateActivation(frame_
)) {
120 activation_animation_
->SetSlideDuration(kActivationCrossfadeDurationMs
);
121 if (mode_
== MODE_ACTIVE
)
122 activation_animation_
->Show();
124 activation_animation_
->Hide();
126 if (mode_
== MODE_ACTIVE
)
127 activation_animation_
->Reset(1);
129 activation_animation_
->Reset(0);
131 initial_paint_
= false;
134 int corner_radius
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
135 0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
138 int active_alpha
= activation_animation_
->CurrentValueBetween(0, 255);
139 paint
.setColor(color_utils::AlphaBlend(active_frame_color_
,
140 inactive_frame_color_
, active_alpha
));
142 TileRoundRect(canvas
, paint
, GetLocalBounds(), corner_radius
);
144 if (!frame_
->IsMaximized() && !frame_
->IsFullscreen() &&
145 mode_
== MODE_INACTIVE
&& !UsesCustomFrameColors()) {
146 PaintHighlightForInactiveRestoredWindow(canvas
);
148 if (frame_
->widget_delegate() &&
149 frame_
->widget_delegate()->ShouldShowWindowTitle()) {
150 PaintTitleBar(canvas
);
152 if (!UsesCustomFrameColors())
153 PaintHeaderContentSeparator(canvas
);
156 void DefaultHeaderPainter::LayoutHeader() {
157 UpdateSizeButtonImages(ShouldUseLightImages());
158 caption_button_container_
->Layout();
160 gfx::Size caption_button_container_size
=
161 caption_button_container_
->GetPreferredSize();
162 caption_button_container_
->SetBounds(
163 view_
->width() - caption_button_container_size
.width(),
165 caption_button_container_size
.width(),
166 caption_button_container_size
.height());
168 LayoutLeftHeaderView();
170 // The header/content separator line overlays the caption buttons.
171 SetHeaderHeightForPainting(caption_button_container_
->height());
174 int DefaultHeaderPainter::GetHeaderHeight() const {
175 return caption_button_container_
->height();
178 int DefaultHeaderPainter::GetHeaderHeightForPainting() const {
179 return painted_height_
;
182 void DefaultHeaderPainter::SetHeaderHeightForPainting(int height
) {
183 painted_height_
= height
;
186 void DefaultHeaderPainter::SchedulePaintForTitle() {
187 view_
->SchedulePaintInRect(GetTitleBounds());
190 void DefaultHeaderPainter::UpdateLeftViewXInset(int left_view_x_inset
) {
191 if (left_view_x_inset_
!= left_view_x_inset
) {
192 left_view_x_inset_
= left_view_x_inset
;
193 LayoutLeftHeaderView();
197 void DefaultHeaderPainter::SetFrameColors(SkColor active_frame_color
,
198 SkColor inactive_frame_color
) {
199 active_frame_color_
= active_frame_color
;
200 inactive_frame_color_
= inactive_frame_color
;
201 UpdateAllButtonImages();
204 void DefaultHeaderPainter::UpdateLeftHeaderView(views::View
* left_header_view
) {
205 left_header_view_
= left_header_view
;
208 ///////////////////////////////////////////////////////////////////////////////
209 // gfx::AnimationDelegate overrides:
211 void DefaultHeaderPainter::AnimationProgressed(
212 const gfx::Animation
* animation
) {
213 view_
->SchedulePaintInRect(GetLocalBounds());
216 ///////////////////////////////////////////////////////////////////////////////
217 // DefaultHeaderPainter, private:
219 void DefaultHeaderPainter::PaintHighlightForInactiveRestoredWindow(
220 gfx::Canvas
* canvas
) {
221 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
222 gfx::ImageSkia top_edge
= *rb
.GetImageSkiaNamed(
223 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP
);
224 gfx::ImageSkia left_edge
= *rb
.GetImageSkiaNamed(
225 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT
);
226 gfx::ImageSkia right_edge
= *rb
.GetImageSkiaNamed(
227 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT
);
228 gfx::ImageSkia bottom_edge
= *rb
.GetImageSkiaNamed(
229 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM
);
231 int left_edge_width
= left_edge
.width();
232 int right_edge_width
= right_edge
.width();
233 canvas
->DrawImageInt(left_edge
, 0, 0);
234 canvas
->DrawImageInt(right_edge
, view_
->width() - right_edge_width
, 0);
235 canvas
->TileImageInt(
239 view_
->width() - left_edge_width
- right_edge_width
,
242 DCHECK_EQ(left_edge
.height(), right_edge
.height());
243 int bottom
= left_edge
.height();
244 int bottom_height
= bottom_edge
.height();
245 canvas
->TileImageInt(
248 bottom
- bottom_height
,
249 view_
->width() - left_edge_width
- right_edge_width
,
253 void DefaultHeaderPainter::PaintTitleBar(gfx::Canvas
* canvas
) {
254 // The window icon is painted by its own views::View.
255 gfx::Rect title_bounds
= GetTitleBounds();
256 title_bounds
.set_x(view_
->GetMirroredXForRect(title_bounds
));
257 canvas
->DrawStringRectWithFlags(frame_
->widget_delegate()->GetWindowTitle(),
261 gfx::Canvas::NO_SUBPIXEL_RENDERING
);
264 void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas
* canvas
) {
265 SkColor color
= (mode_
== MODE_ACTIVE
) ?
266 kHeaderContentSeparatorColor
:
267 kHeaderContentSeparatorInactiveColor
;
270 paint
.setColor(color
);
271 // Draw the line as 1px thick regardless of scale factor.
272 paint
.setStrokeWidth(0);
274 float thickness
= 1 / canvas
->image_scale();
275 SkScalar y
= SkIntToScalar(painted_height_
) - SkFloatToScalar(thickness
);
276 canvas
->sk_canvas()->drawLine(0, y
, SkIntToScalar(view_
->width()), y
, paint
);
279 void DefaultHeaderPainter::LayoutLeftHeaderView() {
280 if (left_header_view_
) {
281 // Vertically center the left header view with respect to the caption button
283 // Floor when computing the center of |caption_button_container_|.
284 gfx::Size size
= left_header_view_
->GetPreferredSize();
285 int icon_offset_y
= caption_button_container_
->height() / 2 -
287 left_header_view_
->SetBounds(
288 left_view_x_inset_
, icon_offset_y
, size
.width(), size
.height());
292 bool DefaultHeaderPainter::ShouldUseLightImages() {
293 int luminance
= color_utils::GetLuminanceForColor(
294 mode_
== MODE_INACTIVE
? inactive_frame_color_
: active_frame_color_
);
295 return luminance
< kMaxLuminanceForLightButtons
;
298 void DefaultHeaderPainter::UpdateAllButtonImages() {
299 bool use_light_images
= ShouldUseLightImages();
300 caption_button_container_
->SetButtonImages(
301 CAPTION_BUTTON_ICON_MINIMIZE
,
302 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_WHITE
303 : IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE
,
304 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
305 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
307 UpdateSizeButtonImages(use_light_images
);
309 caption_button_container_
->SetButtonImages(
310 CAPTION_BUTTON_ICON_CLOSE
,
311 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_WHITE
312 : IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
313 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
314 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
316 caption_button_container_
->SetButtonImages(
317 CAPTION_BUTTON_ICON_LEFT_SNAPPED
,
318 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED_WHITE
319 : IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
320 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
321 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
323 caption_button_container_
->SetButtonImages(
324 CAPTION_BUTTON_ICON_RIGHT_SNAPPED
,
325 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED_WHITE
326 : IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
327 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
328 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
331 void DefaultHeaderPainter::UpdateSizeButtonImages(bool use_light_images
) {
333 if (frame_
->IsMaximized() || frame_
->IsFullscreen()) {
334 icon_id
= use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_WHITE
335 : IDR_AURA_WINDOW_CONTROL_ICON_RESTORE
;
337 icon_id
= use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_WHITE
338 : IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE
;
340 caption_button_container_
->SetButtonImages(
341 CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE
,
343 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
344 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
347 gfx::Rect
DefaultHeaderPainter::GetLocalBounds() const {
348 return gfx::Rect(view_
->width(), painted_height_
);
351 gfx::Rect
DefaultHeaderPainter::GetTitleBounds() const {
352 return HeaderPainterUtil::GetTitleBounds(
353 left_header_view_
, caption_button_container_
, GetTitleFontList());
356 bool DefaultHeaderPainter::UsesCustomFrameColors() const {
357 return active_frame_color_
!= kDefaultFrameColor
||
358 inactive_frame_color_
!= kDefaultFrameColor
;