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)) {
91 DefaultHeaderPainter::~DefaultHeaderPainter() {
94 void DefaultHeaderPainter::Init(
96 views::View
* header_view
,
97 FrameCaptionButtonContainerView
* caption_button_container
) {
100 DCHECK(caption_button_container
);
103 caption_button_container_
= caption_button_container
;
104 UpdateAllButtonImages();
107 int DefaultHeaderPainter::GetMinimumHeaderWidth() const {
108 // Ensure we have enough space for the window icon and buttons. We allow
109 // the title string to collapse to zero width.
110 return GetTitleBounds().x() +
111 caption_button_container_
->GetMinimumSize().width();
114 void DefaultHeaderPainter::PaintHeader(gfx::Canvas
* canvas
, Mode mode
) {
115 Mode old_mode
= mode_
;
118 if (mode_
!= old_mode
) {
119 UpdateAllButtonImages();
120 if (!initial_paint_
&& HeaderPainterUtil::CanAnimateActivation(frame_
)) {
121 activation_animation_
->SetSlideDuration(kActivationCrossfadeDurationMs
);
122 if (mode_
== MODE_ACTIVE
)
123 activation_animation_
->Show();
125 activation_animation_
->Hide();
127 if (mode_
== MODE_ACTIVE
)
128 activation_animation_
->Reset(1);
130 activation_animation_
->Reset(0);
132 initial_paint_
= false;
135 int corner_radius
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
136 0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
139 int active_alpha
= activation_animation_
->CurrentValueBetween(0, 255);
140 paint
.setColor(color_utils::AlphaBlend(active_frame_color_
,
141 inactive_frame_color_
, active_alpha
));
143 TileRoundRect(canvas
, paint
, GetLocalBounds(), corner_radius
);
145 if (!frame_
->IsMaximized() && !frame_
->IsFullscreen() &&
146 mode_
== MODE_INACTIVE
&& !UsesCustomFrameColors()) {
147 PaintHighlightForInactiveRestoredWindow(canvas
);
149 if (frame_
->widget_delegate() &&
150 frame_
->widget_delegate()->ShouldShowWindowTitle()) {
151 PaintTitleBar(canvas
);
153 if (!UsesCustomFrameColors())
154 PaintHeaderContentSeparator(canvas
);
157 void DefaultHeaderPainter::LayoutHeader() {
158 UpdateSizeButtonImages(ShouldUseLightImages());
159 caption_button_container_
->Layout();
161 gfx::Size caption_button_container_size
=
162 caption_button_container_
->GetPreferredSize();
163 caption_button_container_
->SetBounds(
164 view_
->width() - caption_button_container_size
.width(),
166 caption_button_container_size
.width(),
167 caption_button_container_size
.height());
169 LayoutLeftHeaderView();
171 // The header/content separator line overlays the caption buttons.
172 SetHeaderHeightForPainting(caption_button_container_
->height());
175 int DefaultHeaderPainter::GetHeaderHeightForPainting() const {
179 void DefaultHeaderPainter::SetHeaderHeightForPainting(int height
) {
183 void DefaultHeaderPainter::SchedulePaintForTitle() {
184 view_
->SchedulePaintInRect(GetTitleBounds());
187 void DefaultHeaderPainter::UpdateLeftViewXInset(int left_view_x_inset
) {
188 if (left_view_x_inset_
!= left_view_x_inset
) {
189 left_view_x_inset_
= left_view_x_inset
;
190 LayoutLeftHeaderView();
194 void DefaultHeaderPainter::SetFrameColors(SkColor active_frame_color
,
195 SkColor inactive_frame_color
) {
196 active_frame_color_
= active_frame_color
;
197 inactive_frame_color_
= inactive_frame_color
;
198 UpdateAllButtonImages();
201 void DefaultHeaderPainter::UpdateLeftHeaderView(views::View
* left_header_view
) {
202 left_header_view_
= left_header_view
;
205 ///////////////////////////////////////////////////////////////////////////////
206 // gfx::AnimationDelegate overrides:
208 void DefaultHeaderPainter::AnimationProgressed(
209 const gfx::Animation
* animation
) {
210 view_
->SchedulePaintInRect(GetLocalBounds());
213 ///////////////////////////////////////////////////////////////////////////////
214 // DefaultHeaderPainter, private:
216 void DefaultHeaderPainter::PaintHighlightForInactiveRestoredWindow(
217 gfx::Canvas
* canvas
) {
218 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
219 gfx::ImageSkia top_edge
= *rb
.GetImageSkiaNamed(
220 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP
);
221 gfx::ImageSkia left_edge
= *rb
.GetImageSkiaNamed(
222 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT
);
223 gfx::ImageSkia right_edge
= *rb
.GetImageSkiaNamed(
224 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT
);
225 gfx::ImageSkia bottom_edge
= *rb
.GetImageSkiaNamed(
226 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM
);
228 int left_edge_width
= left_edge
.width();
229 int right_edge_width
= right_edge
.width();
230 canvas
->DrawImageInt(left_edge
, 0, 0);
231 canvas
->DrawImageInt(right_edge
, view_
->width() - right_edge_width
, 0);
232 canvas
->TileImageInt(
236 view_
->width() - left_edge_width
- right_edge_width
,
239 DCHECK_EQ(left_edge
.height(), right_edge
.height());
240 int bottom
= left_edge
.height();
241 int bottom_height
= bottom_edge
.height();
242 canvas
->TileImageInt(
245 bottom
- bottom_height
,
246 view_
->width() - left_edge_width
- right_edge_width
,
250 void DefaultHeaderPainter::PaintTitleBar(gfx::Canvas
* canvas
) {
251 // The window icon is painted by its own views::View.
252 gfx::Rect title_bounds
= GetTitleBounds();
253 title_bounds
.set_x(view_
->GetMirroredXForRect(title_bounds
));
254 canvas
->DrawStringRectWithFlags(frame_
->widget_delegate()->GetWindowTitle(),
258 gfx::Canvas::NO_SUBPIXEL_RENDERING
);
261 void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas
* canvas
) {
262 SkColor color
= (mode_
== MODE_ACTIVE
) ?
263 kHeaderContentSeparatorColor
:
264 kHeaderContentSeparatorInactiveColor
;
267 paint
.setColor(color
);
268 // Draw the line as 1px thick regardless of scale factor.
269 paint
.setStrokeWidth(0);
271 float thickness
= 1 / canvas
->image_scale();
272 SkScalar y
= SkIntToScalar(height_
) - SkFloatToScalar(thickness
);
273 canvas
->sk_canvas()->drawLine(0, y
, SkIntToScalar(view_
->width()), y
, paint
);
276 void DefaultHeaderPainter::LayoutLeftHeaderView() {
277 if (left_header_view_
) {
278 // Vertically center the left header view with respect to the caption button
280 // Floor when computing the center of |caption_button_container_|.
281 gfx::Size size
= left_header_view_
->GetPreferredSize();
282 int icon_offset_y
= caption_button_container_
->height() / 2 -
284 left_header_view_
->SetBounds(
285 left_view_x_inset_
, icon_offset_y
, size
.width(), size
.height());
289 bool DefaultHeaderPainter::ShouldUseLightImages() {
290 int luminance
= color_utils::GetLuminanceForColor(
291 mode_
== MODE_INACTIVE
? inactive_frame_color_
: active_frame_color_
);
292 return luminance
< kMaxLuminanceForLightButtons
;
295 void DefaultHeaderPainter::UpdateAllButtonImages() {
296 bool use_light_images
= ShouldUseLightImages();
297 caption_button_container_
->SetButtonImages(
298 CAPTION_BUTTON_ICON_MINIMIZE
,
299 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_WHITE
300 : IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE
,
301 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
302 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
304 UpdateSizeButtonImages(use_light_images
);
306 caption_button_container_
->SetButtonImages(
307 CAPTION_BUTTON_ICON_CLOSE
,
308 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_WHITE
309 : IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
310 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
311 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
313 caption_button_container_
->SetButtonImages(
314 CAPTION_BUTTON_ICON_LEFT_SNAPPED
,
315 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED_WHITE
316 : IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
317 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
318 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
320 caption_button_container_
->SetButtonImages(
321 CAPTION_BUTTON_ICON_RIGHT_SNAPPED
,
322 use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED_WHITE
323 : IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
324 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
325 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
328 void DefaultHeaderPainter::UpdateSizeButtonImages(bool use_light_images
) {
330 if (frame_
->IsMaximized() || frame_
->IsFullscreen()) {
331 icon_id
= use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_WHITE
332 : IDR_AURA_WINDOW_CONTROL_ICON_RESTORE
;
334 icon_id
= use_light_images
? IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_WHITE
335 : IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE
;
337 caption_button_container_
->SetButtonImages(
338 CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE
,
340 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
341 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
344 gfx::Rect
DefaultHeaderPainter::GetLocalBounds() const {
345 return gfx::Rect(view_
->width(), height_
);
348 gfx::Rect
DefaultHeaderPainter::GetTitleBounds() const {
349 return HeaderPainterUtil::GetTitleBounds(
350 left_header_view_
, caption_button_container_
, GetTitleFontList());
353 bool DefaultHeaderPainter::UsesCustomFrameColors() const {
354 return active_frame_color_
!= kDefaultFrameColor
||
355 inactive_frame_color_
!= kDefaultFrameColor
;