• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/frame/default_header_painter.h"
6 
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"
26 
27 using views::Widget;
28 
29 namespace {
30 
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;
40 
41 // Tiles an image into an area, rounding the top corners.
TileRoundRect(gfx::Canvas * canvas,const gfx::ImageSkia & image,const SkPaint & paint,const gfx::Rect & bounds,int corner_radius)42 void TileRoundRect(gfx::Canvas* canvas,
43                    const gfx::ImageSkia& image,
44                    const SkPaint& paint,
45                    const gfx::Rect& bounds,
46                    int corner_radius) {
47   SkRect rect = gfx::RectToSkRect(bounds);
48   const SkScalar corner_radius_scalar = SkIntToScalar(corner_radius);
49   SkScalar radii[8] = {
50       corner_radius_scalar, corner_radius_scalar,  // top-left
51       corner_radius_scalar, corner_radius_scalar,  // top-right
52       0, 0,   // bottom-right
53       0, 0};  // bottom-left
54   SkPath path;
55   path.addRoundRect(rect, radii, SkPath::kCW_Direction);
56   canvas->DrawImageInPath(image, 0, 0, path, paint);
57 }
58 
59 // Returns the FontList to use for the title.
GetTitleFontList()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;
65 }
66 
67 }  // namespace
68 
69 namespace ash {
70 
71 ///////////////////////////////////////////////////////////////////////////////
72 // DefaultHeaderPainter, public:
73 
DefaultHeaderPainter()74 DefaultHeaderPainter::DefaultHeaderPainter()
75     : frame_(NULL),
76       view_(NULL),
77       window_icon_(NULL),
78       window_icon_size_(HeaderPainterUtil::GetDefaultIconSize()),
79       caption_button_container_(NULL),
80       height_(0),
81       mode_(MODE_INACTIVE),
82       initial_paint_(true),
83       activation_animation_(new gfx::SlideAnimation(this)) {
84 }
85 
~DefaultHeaderPainter()86 DefaultHeaderPainter::~DefaultHeaderPainter() {
87 }
88 
Init(views::Widget * frame,views::View * header_view,views::View * window_icon,FrameCaptionButtonContainerView * caption_button_container)89 void DefaultHeaderPainter::Init(
90     views::Widget* frame,
91     views::View* header_view,
92     views::View* window_icon,
93     FrameCaptionButtonContainerView* caption_button_container) {
94   DCHECK(frame);
95   DCHECK(header_view);
96   // window_icon may be NULL.
97   DCHECK(caption_button_container);
98   frame_ = frame;
99   view_ = header_view;
100   window_icon_ = window_icon;
101   caption_button_container_ = caption_button_container;
102 
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);
121 
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);
137 }
138 
GetMinimumHeaderWidth() const139 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();
144 }
145 
PaintHeader(gfx::Canvas * canvas,Mode mode)146 void DefaultHeaderPainter::PaintHeader(gfx::Canvas* canvas, Mode mode) {
147   Mode old_mode = mode_;
148   mode_ = mode;
149 
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();
155       else
156         activation_animation_->Hide();
157     } else {
158       if (mode_ == MODE_ACTIVE)
159         activation_animation_->Reset(1);
160       else
161         activation_animation_->Reset(0);
162     }
163     initial_paint_ = false;
164   }
165 
166   int corner_radius = (frame_->IsMaximized() || frame_->IsFullscreen()) ?
167       0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
168 
169   int active_alpha = activation_animation_->CurrentValueBetween(0, 255);
170   int inactive_alpha = 255 - active_alpha;
171 
172   SkPaint paint;
173   if (inactive_alpha > 0) {
174     if (active_alpha > 0)
175       paint.setXfermodeMode(SkXfermode::kPlus_Mode);
176 
177     paint.setAlpha(inactive_alpha);
178     gfx::ImageSkia inactive_frame = *GetInactiveFrameImage();
179     TileRoundRect(canvas, inactive_frame, paint, GetLocalBounds(),
180         corner_radius);
181   }
182 
183   if (active_alpha > 0) {
184     paint.setAlpha(active_alpha);
185     gfx::ImageSkia active_frame = *GetActiveFrameImage();
186     TileRoundRect(canvas, active_frame, paint, GetLocalBounds(),
187         corner_radius);
188   }
189 
190   if (!frame_->IsMaximized() &&
191       !frame_->IsFullscreen() &&
192       mode_ == MODE_INACTIVE) {
193     PaintHighlightForInactiveRestoredWindow(canvas);
194   }
195   if (frame_->widget_delegate() &&
196       frame_->widget_delegate()->ShouldShowWindowTitle()) {
197     PaintTitleBar(canvas);
198   }
199   PaintHeaderContentSeparator(canvas);
200 }
201 
LayoutHeader()202 void DefaultHeaderPainter::LayoutHeader() {
203   caption_button_container_->Layout();
204 
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(),
209       0,
210       caption_button_container_size.width(),
211       caption_button_container_size.height());
212 
213   if (window_icon_) {
214     // Vertically center the window icon with respect to the caption button
215     // container.
216     // Floor when computing the center of |caption_button_container_|.
217     int icon_offset_y =
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_);
221   }
222 
223   // The header/content separator line overlays the caption buttons.
224   SetHeaderHeightForPainting(caption_button_container_->height());
225 }
226 
GetHeaderHeightForPainting() const227 int DefaultHeaderPainter::GetHeaderHeightForPainting() const {
228   return height_;
229 }
230 
SetHeaderHeightForPainting(int height)231 void DefaultHeaderPainter::SetHeaderHeightForPainting(int height) {
232   height_ = height;
233 }
234 
SchedulePaintForTitle()235 void DefaultHeaderPainter::SchedulePaintForTitle() {
236   view_->SchedulePaintInRect(GetTitleBounds());
237 }
238 
UpdateWindowIcon(views::View * window_icon,int window_icon_size)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;
243 }
244 
245 ///////////////////////////////////////////////////////////////////////////////
246 // gfx::AnimationDelegate overrides:
247 
AnimationProgressed(const gfx::Animation * animation)248 void DefaultHeaderPainter::AnimationProgressed(
249     const gfx::Animation* animation) {
250   view_->SchedulePaintInRect(GetLocalBounds());
251 }
252 
253 ///////////////////////////////////////////////////////////////////////////////
254 // DefaultHeaderPainter, private:
255 
PaintHighlightForInactiveRestoredWindow(gfx::Canvas * canvas)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);
267 
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(
273       top_edge,
274       left_edge_width,
275       0,
276       view_->width() - left_edge_width - right_edge_width,
277       top_edge.height());
278 
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(
283       bottom_edge,
284       left_edge_width,
285       bottom - bottom_height,
286       view_->width() - left_edge_width - right_edge_width,
287       bottom_height);
288 }
289 
PaintTitleBar(gfx::Canvas * canvas)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(),
295                                   GetTitleFontList(),
296                                   kTitleTextColor,
297                                   title_bounds,
298                                   gfx::Canvas::NO_SUBPIXEL_RENDERING);
299 }
300 
PaintHeaderContentSeparator(gfx::Canvas * canvas)301 void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas* canvas) {
302   SkColor color = (mode_ == MODE_ACTIVE) ?
303       kHeaderContentSeparatorColor :
304       kHeaderContentSeparatorInactiveColor;
305 
306   SkPaint paint;
307   paint.setColor(color);
308   // Draw the line as 1px thick regardless of scale factor.
309   paint.setStrokeWidth(0);
310 
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);
314 }
315 
GetLocalBounds() const316 gfx::Rect DefaultHeaderPainter::GetLocalBounds() const {
317   return gfx::Rect(view_->width(), height_);
318 }
319 
GetTitleBounds() const320 gfx::Rect DefaultHeaderPainter::GetTitleBounds() const {
321   return HeaderPainterUtil::GetTitleBounds(
322       window_icon_, caption_button_container_, GetTitleFontList());
323 }
324 
GetActiveFrameImage() const325 gfx::ImageSkia* DefaultHeaderPainter::GetActiveFrameImage() const {
326   return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
327       IDR_AURA_WINDOW_HEADER_BASE);
328 }
329 
GetInactiveFrameImage() const330 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(
335       frame_image_id);
336 }
337 
338 }  // namespace ash
339