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/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 // The color of the frame.
39 const SkColor kFrameColor
= SkColorSetRGB(242, 242, 242);
40 // The alpha of the inactive frame.
41 const SkAlpha kInactiveFrameAlpha
= 204;
42 // Duration of crossfade animation for activating and deactivating frame.
43 const int kActivationCrossfadeDurationMs
= 200;
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 caption_button_container_(NULL
),
86 activation_animation_(new gfx::SlideAnimation(this)) {
89 DefaultHeaderPainter::~DefaultHeaderPainter() {
92 void DefaultHeaderPainter::Init(
94 views::View
* header_view
,
95 FrameCaptionButtonContainerView
* caption_button_container
) {
98 DCHECK(caption_button_container
);
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 UpdateSizeButtonImages();
110 caption_button_container_
->SetButtonImages(
111 CAPTION_BUTTON_ICON_CLOSE
,
112 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE
,
113 IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I
,
114 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
115 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
117 // There is no dedicated icon for the snap-left and snap-right buttons
118 // when |frame_| is inactive because they should never be visible while
119 // |frame_| is inactive.
120 caption_button_container_
->SetButtonImages(
121 CAPTION_BUTTON_ICON_LEFT_SNAPPED
,
122 IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
123 IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED
,
124 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
125 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
126 caption_button_container_
->SetButtonImages(
127 CAPTION_BUTTON_ICON_RIGHT_SNAPPED
,
128 IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
129 IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED
,
130 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
131 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
134 int DefaultHeaderPainter::GetMinimumHeaderWidth() const {
135 // Ensure we have enough space for the window icon and buttons. We allow
136 // the title string to collapse to zero width.
137 return GetTitleBounds().x() +
138 caption_button_container_
->GetMinimumSize().width();
141 void DefaultHeaderPainter::PaintHeader(gfx::Canvas
* canvas
, Mode mode
) {
142 Mode old_mode
= mode_
;
145 if (mode_
!= old_mode
) {
146 if (!initial_paint_
&& HeaderPainterUtil::CanAnimateActivation(frame_
)) {
147 activation_animation_
->SetSlideDuration(kActivationCrossfadeDurationMs
);
148 if (mode_
== MODE_ACTIVE
)
149 activation_animation_
->Show();
151 activation_animation_
->Hide();
153 if (mode_
== MODE_ACTIVE
)
154 activation_animation_
->Reset(1);
156 activation_animation_
->Reset(0);
158 initial_paint_
= false;
161 int corner_radius
= (frame_
->IsMaximized() || frame_
->IsFullscreen()) ?
162 0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
165 int active_alpha
= activation_animation_
->CurrentValueBetween(0, 255);
166 paint
.setColor(color_utils::AlphaBlend(
167 kFrameColor
, GetInactiveFrameColor(), active_alpha
));
169 TileRoundRect(canvas
, paint
, GetLocalBounds(), corner_radius
);
171 if (!frame_
->IsMaximized() &&
172 !frame_
->IsFullscreen() &&
173 mode_
== MODE_INACTIVE
) {
174 PaintHighlightForInactiveRestoredWindow(canvas
);
176 if (frame_
->widget_delegate() &&
177 frame_
->widget_delegate()->ShouldShowWindowTitle()) {
178 PaintTitleBar(canvas
);
180 PaintHeaderContentSeparator(canvas
);
183 void DefaultHeaderPainter::LayoutHeader() {
184 UpdateSizeButtonImages();
185 caption_button_container_
->Layout();
187 gfx::Size caption_button_container_size
=
188 caption_button_container_
->GetPreferredSize();
189 caption_button_container_
->SetBounds(
190 view_
->width() - caption_button_container_size
.width(),
192 caption_button_container_size
.width(),
193 caption_button_container_size
.height());
195 LayoutLeftHeaderView();
197 // The header/content separator line overlays the caption buttons.
198 SetHeaderHeightForPainting(caption_button_container_
->height());
201 int DefaultHeaderPainter::GetHeaderHeightForPainting() const {
205 void DefaultHeaderPainter::SetHeaderHeightForPainting(int height
) {
209 void DefaultHeaderPainter::SchedulePaintForTitle() {
210 view_
->SchedulePaintInRect(GetTitleBounds());
213 void DefaultHeaderPainter::UpdateLeftViewXInset(int left_view_x_inset
) {
214 if (left_view_x_inset_
!= left_view_x_inset
) {
215 left_view_x_inset_
= left_view_x_inset
;
216 LayoutLeftHeaderView();
220 void DefaultHeaderPainter::UpdateLeftHeaderView(views::View
* left_header_view
) {
221 left_header_view_
= left_header_view
;
224 ///////////////////////////////////////////////////////////////////////////////
225 // gfx::AnimationDelegate overrides:
227 void DefaultHeaderPainter::AnimationProgressed(
228 const gfx::Animation
* animation
) {
229 view_
->SchedulePaintInRect(GetLocalBounds());
232 ///////////////////////////////////////////////////////////////////////////////
233 // DefaultHeaderPainter, private:
235 void DefaultHeaderPainter::PaintHighlightForInactiveRestoredWindow(
236 gfx::Canvas
* canvas
) {
237 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
238 gfx::ImageSkia top_edge
= *rb
.GetImageSkiaNamed(
239 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP
);
240 gfx::ImageSkia left_edge
= *rb
.GetImageSkiaNamed(
241 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT
);
242 gfx::ImageSkia right_edge
= *rb
.GetImageSkiaNamed(
243 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT
);
244 gfx::ImageSkia bottom_edge
= *rb
.GetImageSkiaNamed(
245 IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM
);
247 int left_edge_width
= left_edge
.width();
248 int right_edge_width
= right_edge
.width();
249 canvas
->DrawImageInt(left_edge
, 0, 0);
250 canvas
->DrawImageInt(right_edge
, view_
->width() - right_edge_width
, 0);
251 canvas
->TileImageInt(
255 view_
->width() - left_edge_width
- right_edge_width
,
258 DCHECK_EQ(left_edge
.height(), right_edge
.height());
259 int bottom
= left_edge
.height();
260 int bottom_height
= bottom_edge
.height();
261 canvas
->TileImageInt(
264 bottom
- bottom_height
,
265 view_
->width() - left_edge_width
- right_edge_width
,
269 void DefaultHeaderPainter::PaintTitleBar(gfx::Canvas
* canvas
) {
270 // The window icon is painted by its own views::View.
271 gfx::Rect title_bounds
= GetTitleBounds();
272 title_bounds
.set_x(view_
->GetMirroredXForRect(title_bounds
));
273 canvas
->DrawStringRectWithFlags(frame_
->widget_delegate()->GetWindowTitle(),
277 gfx::Canvas::NO_SUBPIXEL_RENDERING
);
280 void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas
* canvas
) {
281 SkColor color
= (mode_
== MODE_ACTIVE
) ?
282 kHeaderContentSeparatorColor
:
283 kHeaderContentSeparatorInactiveColor
;
286 paint
.setColor(color
);
287 // Draw the line as 1px thick regardless of scale factor.
288 paint
.setStrokeWidth(0);
290 float thickness
= 1 / canvas
->image_scale();
291 SkScalar y
= SkIntToScalar(height_
) - SkFloatToScalar(thickness
);
292 canvas
->sk_canvas()->drawLine(0, y
, SkIntToScalar(view_
->width()), y
, paint
);
295 void DefaultHeaderPainter::LayoutLeftHeaderView() {
296 if (left_header_view_
) {
297 // Vertically center the left header view with respect to the caption button
299 // Floor when computing the center of |caption_button_container_|.
300 gfx::Size size
= left_header_view_
->GetPreferredSize();
301 int icon_offset_y
= caption_button_container_
->height() / 2 -
303 left_header_view_
->SetBounds(
304 left_view_x_inset_
, icon_offset_y
, size
.width(), size
.height());
308 void DefaultHeaderPainter::UpdateSizeButtonImages() {
310 int inactive_icon_id
= 0;
311 if (frame_
->IsMaximized() || frame_
->IsFullscreen()) {
312 icon_id
= IDR_AURA_WINDOW_CONTROL_ICON_RESTORE
;
313 inactive_icon_id
= IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_I
;
315 icon_id
= IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE
;
316 inactive_icon_id
= IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_I
;
318 caption_button_container_
->SetButtonImages(
319 CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE
,
322 IDR_AURA_WINDOW_CONTROL_BACKGROUND_H
,
323 IDR_AURA_WINDOW_CONTROL_BACKGROUND_P
);
326 gfx::Rect
DefaultHeaderPainter::GetLocalBounds() const {
327 return gfx::Rect(view_
->width(), height_
);
330 gfx::Rect
DefaultHeaderPainter::GetTitleBounds() const {
331 return HeaderPainterUtil::GetTitleBounds(
332 left_header_view_
, caption_button_container_
, GetTitleFontList());
335 SkColor
DefaultHeaderPainter::GetInactiveFrameColor() const {
336 SkColor color
= kFrameColor
;
337 if (!frame_
->IsMaximized() && !frame_
->IsFullscreen()) {
338 color
= SkColorSetARGB(kInactiveFrameAlpha
,