OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/wm/header_painter.h" | 5 #include "ash/wm/header_painter.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "ash/root_window_controller.h" | 9 #include "ash/root_window_controller.h" |
10 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" | 10 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" |
11 #include "ash/wm/solo_window_tracker.h" | |
12 #include "base/logging.h" // DCHECK | 11 #include "base/logging.h" // DCHECK |
13 #include "grit/ash_resources.h" | 12 #include "grit/ash_resources.h" |
14 #include "third_party/skia/include/core/SkCanvas.h" | 13 #include "third_party/skia/include/core/SkCanvas.h" |
15 #include "third_party/skia/include/core/SkColor.h" | 14 #include "third_party/skia/include/core/SkColor.h" |
16 #include "third_party/skia/include/core/SkPaint.h" | 15 #include "third_party/skia/include/core/SkPaint.h" |
17 #include "third_party/skia/include/core/SkPath.h" | 16 #include "third_party/skia/include/core/SkPath.h" |
18 #include "ui/aura/window.h" | 17 #include "ui/aura/window.h" |
19 #include "ui/base/hit_test.h" | 18 #include "ui/base/hit_test.h" |
20 #include "ui/base/resource/resource_bundle.h" | 19 #include "ui/base/resource/resource_bundle.h" |
21 #include "ui/base/theme_provider.h" | 20 #include "ui/base/theme_provider.h" |
(...skipping 30 matching lines...) Expand all Loading... |
52 const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(128, 128, 128); | 51 const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(128, 128, 128); |
53 // In the pre-Ash era the web content area had a frame along the left edge, so | 52 // In the pre-Ash era the web content area had a frame along the left edge, so |
54 // user-generated theme images for the new tab page assume they are shifted | 53 // user-generated theme images for the new tab page assume they are shifted |
55 // right relative to the header. Now that we have removed the left edge frame | 54 // right relative to the header. Now that we have removed the left edge frame |
56 // we need to copy the theme image for the window header from a few pixels | 55 // we need to copy the theme image for the window header from a few pixels |
57 // inset to preserve alignment with the NTP image, or else we'll break a bunch | 56 // inset to preserve alignment with the NTP image, or else we'll break a bunch |
58 // of existing themes. We do something similar on OS X for the same reason. | 57 // of existing themes. We do something similar on OS X for the same reason. |
59 const int kThemeFrameImageInsetX = 5; | 58 const int kThemeFrameImageInsetX = 5; |
60 // Duration of crossfade animation for activating and deactivating frame. | 59 // Duration of crossfade animation for activating and deactivating frame. |
61 const int kActivationCrossfadeDurationMs = 200; | 60 const int kActivationCrossfadeDurationMs = 200; |
62 // Alpha/opacity value for fully-opaque headers. | |
63 const int kFullyOpaque = 255; | |
64 | 61 |
65 // Tiles an image into an area, rounding the top corners. Samples |image| | 62 // Tiles an image into an area, rounding the top corners. Samples |image| |
66 // starting |image_inset_x| pixels from the left of the image. | 63 // starting |image_inset_x| pixels from the left of the image. |
67 void TileRoundRect(gfx::Canvas* canvas, | 64 void TileRoundRect(gfx::Canvas* canvas, |
68 const gfx::ImageSkia& image, | 65 const gfx::ImageSkia& image, |
69 const SkPaint& paint, | 66 const SkPaint& paint, |
70 const gfx::Rect& bounds, | 67 const gfx::Rect& bounds, |
71 int top_left_corner_radius, | 68 int top_left_corner_radius, |
72 int top_right_corner_radius, | 69 int top_right_corner_radius, |
73 int image_inset_x) { | 70 int image_inset_x) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 temporary_canvas.DrawImageInt(*frame_overlay_image, 0, 0); | 125 temporary_canvas.DrawImageInt(*frame_overlay_image, 0, 0); |
129 TileRoundRect(canvas, gfx::ImageSkia(temporary_canvas.ExtractImageRep()), | 126 TileRoundRect(canvas, gfx::ImageSkia(temporary_canvas.ExtractImageRep()), |
130 paint, bounds, corner_radius, corner_radius, 0); | 127 paint, bounds, corner_radius, corner_radius, 0); |
131 } | 128 } |
132 } | 129 } |
133 | 130 |
134 } // namespace | 131 } // namespace |
135 | 132 |
136 namespace ash { | 133 namespace ash { |
137 | 134 |
138 // static | |
139 int HeaderPainter::kActiveWindowOpacity = 255; // 1.0 | |
140 int HeaderPainter::kInactiveWindowOpacity = 255; // 1.0 | |
141 int HeaderPainter::kSoloWindowOpacity = 77; // 0.3 | |
142 | |
143 /////////////////////////////////////////////////////////////////////////////// | 135 /////////////////////////////////////////////////////////////////////////////// |
144 // HeaderPainter, public: | 136 // HeaderPainter, public: |
145 | 137 |
146 HeaderPainter::HeaderPainter() | 138 HeaderPainter::HeaderPainter() |
147 : frame_(NULL), | 139 : frame_(NULL), |
148 header_view_(NULL), | 140 header_view_(NULL), |
149 window_icon_(NULL), | 141 window_icon_(NULL), |
150 caption_button_container_(NULL), | 142 caption_button_container_(NULL), |
151 window_(NULL), | 143 window_(NULL), |
152 header_height_(0), | 144 header_height_(0), |
153 top_left_corner_(NULL), | 145 top_left_corner_(NULL), |
154 top_edge_(NULL), | 146 top_edge_(NULL), |
155 top_right_corner_(NULL), | 147 top_right_corner_(NULL), |
156 header_left_edge_(NULL), | 148 header_left_edge_(NULL), |
157 header_right_edge_(NULL), | 149 header_right_edge_(NULL), |
158 previous_theme_frame_id_(0), | 150 previous_theme_frame_id_(0), |
159 previous_theme_frame_overlay_id_(0), | 151 previous_theme_frame_overlay_id_(0), |
160 previous_opacity_(0), | |
161 crossfade_theme_frame_id_(0), | 152 crossfade_theme_frame_id_(0), |
162 crossfade_theme_frame_overlay_id_(0), | 153 crossfade_theme_frame_overlay_id_(0) {} |
163 crossfade_opacity_(0) {} | |
164 | 154 |
165 HeaderPainter::~HeaderPainter() { | 155 HeaderPainter::~HeaderPainter() { |
166 // Sometimes we are destroyed before the window closes, so ensure we clean up. | 156 // Sometimes we are destroyed before the window closes, so ensure we clean up. |
167 if (window_) | 157 if (window_) |
168 window_->RemoveObserver(this); | 158 window_->RemoveObserver(this); |
169 } | 159 } |
170 | 160 |
171 void HeaderPainter::Init( | 161 void HeaderPainter::Init( |
172 views::Widget* frame, | 162 views::Widget* frame, |
173 views::View* header_view, | 163 views::View* header_view, |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 | 242 |
253 int HeaderPainter::GetRightInset() const { | 243 int HeaderPainter::GetRightInset() const { |
254 return caption_button_container_->GetPreferredSize().width(); | 244 return caption_button_container_->GetPreferredSize().width(); |
255 } | 245 } |
256 | 246 |
257 int HeaderPainter::GetThemeBackgroundXInset() const { | 247 int HeaderPainter::GetThemeBackgroundXInset() const { |
258 return kThemeFrameImageInsetX; | 248 return kThemeFrameImageInsetX; |
259 } | 249 } |
260 | 250 |
261 void HeaderPainter::PaintHeader(gfx::Canvas* canvas, | 251 void HeaderPainter::PaintHeader(gfx::Canvas* canvas, |
262 HeaderMode header_mode, | |
263 int theme_frame_id, | 252 int theme_frame_id, |
264 int theme_frame_overlay_id) { | 253 int theme_frame_overlay_id) { |
265 bool initial_paint = (previous_theme_frame_id_ == 0); | 254 bool initial_paint = (previous_theme_frame_id_ == 0); |
266 if (!initial_paint && | 255 if (!initial_paint && |
267 (previous_theme_frame_id_ != theme_frame_id || | 256 (previous_theme_frame_id_ != theme_frame_id || |
268 previous_theme_frame_overlay_id_ != theme_frame_overlay_id)) { | 257 previous_theme_frame_overlay_id_ != theme_frame_overlay_id)) { |
269 aura::Window* parent = frame_->GetNativeWindow()->parent(); | 258 aura::Window* parent = frame_->GetNativeWindow()->parent(); |
270 // Don't animate the header if the parent (a workspace) is already | 259 // Don't animate the header if the parent (a workspace) is already |
271 // animating. Doing so results in continually painting during the animation | 260 // animating. Doing so results in continually painting during the animation |
272 // and gives a slower frame rate. | 261 // and gives a slower frame rate. |
273 // TODO(sky): expose a better way to determine this rather than assuming | 262 // TODO(sky): expose a better way to determine this rather than assuming |
274 // the parent is a workspace. | 263 // the parent is a workspace. |
275 bool parent_animating = parent && | 264 bool parent_animating = parent && |
276 (parent->layer()->GetAnimator()->IsAnimatingProperty( | 265 (parent->layer()->GetAnimator()->IsAnimatingProperty( |
277 ui::LayerAnimationElement::OPACITY) || | 266 ui::LayerAnimationElement::OPACITY) || |
278 parent->layer()->GetAnimator()->IsAnimatingProperty( | 267 parent->layer()->GetAnimator()->IsAnimatingProperty( |
279 ui::LayerAnimationElement::VISIBILITY)); | 268 ui::LayerAnimationElement::VISIBILITY)); |
280 if (!parent_animating) { | 269 if (!parent_animating) { |
281 crossfade_animation_.reset(new gfx::SlideAnimation(this)); | 270 crossfade_animation_.reset(new gfx::SlideAnimation(this)); |
282 crossfade_theme_frame_id_ = previous_theme_frame_id_; | 271 crossfade_theme_frame_id_ = previous_theme_frame_id_; |
283 crossfade_theme_frame_overlay_id_ = previous_theme_frame_overlay_id_; | 272 crossfade_theme_frame_overlay_id_ = previous_theme_frame_overlay_id_; |
284 crossfade_opacity_ = previous_opacity_; | |
285 crossfade_animation_->SetSlideDuration(kActivationCrossfadeDurationMs); | 273 crossfade_animation_->SetSlideDuration(kActivationCrossfadeDurationMs); |
286 crossfade_animation_->Show(); | 274 crossfade_animation_->Show(); |
287 } else { | 275 } else { |
288 crossfade_animation_.reset(); | 276 crossfade_animation_.reset(); |
289 } | 277 } |
290 } | 278 } |
291 | 279 |
292 int opacity = | |
293 GetHeaderOpacity(header_mode, theme_frame_id, theme_frame_overlay_id); | |
294 ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); | 280 ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); |
295 gfx::ImageSkia* theme_frame = theme_provider->GetImageSkiaNamed( | 281 gfx::ImageSkia* theme_frame = theme_provider->GetImageSkiaNamed( |
296 theme_frame_id); | 282 theme_frame_id); |
297 gfx::ImageSkia* theme_frame_overlay = NULL; | 283 gfx::ImageSkia* theme_frame_overlay = NULL; |
298 if (theme_frame_overlay_id != 0) { | 284 if (theme_frame_overlay_id != 0) { |
299 theme_frame_overlay = theme_provider->GetImageSkiaNamed( | 285 theme_frame_overlay = theme_provider->GetImageSkiaNamed( |
300 theme_frame_overlay_id); | 286 theme_frame_overlay_id); |
301 } | 287 } |
302 | 288 |
303 int corner_radius = GetHeaderCornerRadius(); | 289 int corner_radius = GetHeaderCornerRadius(); |
304 SkPaint paint; | 290 SkPaint paint; |
305 | 291 |
306 if (crossfade_animation_.get() && crossfade_animation_->is_animating()) { | 292 if (crossfade_animation_.get() && crossfade_animation_->is_animating()) { |
307 gfx::ImageSkia* crossfade_theme_frame = | 293 gfx::ImageSkia* crossfade_theme_frame = |
308 theme_provider->GetImageSkiaNamed(crossfade_theme_frame_id_); | 294 theme_provider->GetImageSkiaNamed(crossfade_theme_frame_id_); |
309 gfx::ImageSkia* crossfade_theme_frame_overlay = NULL; | 295 gfx::ImageSkia* crossfade_theme_frame_overlay = NULL; |
310 if (crossfade_theme_frame_overlay_id_ != 0) { | 296 if (crossfade_theme_frame_overlay_id_ != 0) { |
311 crossfade_theme_frame_overlay = theme_provider->GetImageSkiaNamed( | 297 crossfade_theme_frame_overlay = theme_provider->GetImageSkiaNamed( |
312 crossfade_theme_frame_overlay_id_); | 298 crossfade_theme_frame_overlay_id_); |
313 } | 299 } |
314 if (!crossfade_theme_frame || | 300 if (!crossfade_theme_frame || |
315 (crossfade_theme_frame_overlay_id_ != 0 && | 301 (crossfade_theme_frame_overlay_id_ != 0 && |
316 !crossfade_theme_frame_overlay)) { | 302 !crossfade_theme_frame_overlay)) { |
317 // Reset the animation. This case occurs when the user switches the theme | 303 // Reset the animation. This case occurs when the user switches the theme |
318 // that they are using. | 304 // that they are using. |
319 crossfade_animation_.reset(); | 305 crossfade_animation_.reset(); |
320 paint.setAlpha(opacity); | |
321 } else { | 306 } else { |
322 double current_value = crossfade_animation_->GetCurrentValue(); | 307 int old_alpha = crossfade_animation_->CurrentValueBetween(255, 0); |
323 int old_alpha = (1 - current_value) * crossfade_opacity_; | 308 int new_alpha = 255 - old_alpha; |
324 int new_alpha = current_value * opacity; | |
325 | 309 |
326 // Draw the old header background, clipping the corners to be rounded. | 310 // Draw the old header background, clipping the corners to be rounded. |
327 paint.setAlpha(old_alpha); | 311 paint.setAlpha(old_alpha); |
328 paint.setXfermodeMode(SkXfermode::kPlus_Mode); | 312 paint.setXfermodeMode(SkXfermode::kPlus_Mode); |
329 PaintFrameImagesInRoundRect(canvas, | 313 PaintFrameImagesInRoundRect(canvas, |
330 crossfade_theme_frame, | 314 crossfade_theme_frame, |
331 crossfade_theme_frame_overlay, | 315 crossfade_theme_frame_overlay, |
332 paint, | 316 paint, |
333 GetHeaderLocalBounds(), | 317 GetHeaderLocalBounds(), |
334 corner_radius, | 318 corner_radius, |
335 GetThemeBackgroundXInset()); | 319 GetThemeBackgroundXInset()); |
336 | 320 |
337 paint.setAlpha(new_alpha); | 321 paint.setAlpha(new_alpha); |
338 } | 322 } |
339 } else { | |
340 paint.setAlpha(opacity); | |
341 } | 323 } |
342 | 324 |
343 // Draw the header background, clipping the corners to be rounded. | 325 // Draw the header background, clipping the corners to be rounded. |
344 PaintFrameImagesInRoundRect(canvas, | 326 PaintFrameImagesInRoundRect(canvas, |
345 theme_frame, | 327 theme_frame, |
346 theme_frame_overlay, | 328 theme_frame_overlay, |
347 paint, | 329 paint, |
348 GetHeaderLocalBounds(), | 330 GetHeaderLocalBounds(), |
349 corner_radius, | 331 corner_radius, |
350 GetThemeBackgroundXInset()); | 332 GetThemeBackgroundXInset()); |
351 | 333 |
352 previous_theme_frame_id_ = theme_frame_id; | 334 previous_theme_frame_id_ = theme_frame_id; |
353 previous_theme_frame_overlay_id_ = theme_frame_overlay_id; | 335 previous_theme_frame_overlay_id_ = theme_frame_overlay_id; |
354 previous_opacity_ = opacity; | |
355 | 336 |
356 // We don't need the extra lightness in the edges when we're at the top edge | 337 // We don't need the extra lightness in the edges when we're at the top edge |
357 // of the screen or when the header's corners are not rounded. | 338 // of the screen or when the header's corners are not rounded. |
358 // | 339 // |
359 // TODO(sky): this isn't quite right. What we really want is a method that | 340 // TODO(sky): this isn't quite right. What we really want is a method that |
360 // returns bounds ignoring transforms on certain windows (such as workspaces) | 341 // returns bounds ignoring transforms on certain windows (such as workspaces) |
361 // and is relative to the root. | 342 // and is relative to the root. |
362 if (frame_->GetNativeWindow()->bounds().y() == 0 || corner_radius == 0) | 343 if (frame_->GetNativeWindow()->bounds().y() == 0 || corner_radius == 0) |
363 return; | 344 return; |
364 | 345 |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
522 return caption_button_container_->y() + | 503 return caption_button_container_->y() + |
523 caption_button_container_->height() / 2; | 504 caption_button_container_->height() / 2; |
524 } | 505 } |
525 | 506 |
526 int HeaderPainter::GetHeaderCornerRadius() const { | 507 int HeaderPainter::GetHeaderCornerRadius() const { |
527 bool square_corners = (frame_->IsMaximized() || frame_->IsFullscreen()); | 508 bool square_corners = (frame_->IsMaximized() || frame_->IsFullscreen()); |
528 const int kCornerRadius = 2; | 509 const int kCornerRadius = 2; |
529 return square_corners ? 0 : kCornerRadius; | 510 return square_corners ? 0 : kCornerRadius; |
530 } | 511 } |
531 | 512 |
532 int HeaderPainter::GetHeaderOpacity( | |
533 HeaderMode header_mode, | |
534 int theme_frame_id, | |
535 int theme_frame_overlay_id) const { | |
536 // User-provided themes are painted fully opaque. | |
537 ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); | |
538 if (theme_provider->HasCustomImage(theme_frame_id) || | |
539 (theme_frame_overlay_id != 0 && | |
540 theme_provider->HasCustomImage(theme_frame_overlay_id))) { | |
541 return kFullyOpaque; | |
542 } | |
543 | |
544 // Maximized and fullscreen windows are fully opaque. | |
545 if (frame_->IsMaximized() || frame_->IsFullscreen()) | |
546 return kFullyOpaque; | |
547 | |
548 // Solo header is very transparent. | |
549 ash::SoloWindowTracker* solo_window_tracker = | |
550 internal::RootWindowController::ForWindow(window_)->solo_window_tracker(); | |
551 if (solo_window_tracker && | |
552 solo_window_tracker->GetWindowWithSoloHeader() == window_) { | |
553 return kSoloWindowOpacity; | |
554 } | |
555 | |
556 // Otherwise, change transparency based on window activation status. | |
557 if (header_mode == ACTIVE) | |
558 return kActiveWindowOpacity; | |
559 return kInactiveWindowOpacity; | |
560 } | |
561 | |
562 void HeaderPainter::SchedulePaintForHeader() { | 513 void HeaderPainter::SchedulePaintForHeader() { |
563 int top_left_height = top_left_corner_->height(); | 514 int top_left_height = top_left_corner_->height(); |
564 int top_right_height = top_right_corner_->height(); | 515 int top_right_height = top_right_corner_->height(); |
565 header_view_->SchedulePaintInRect( | 516 header_view_->SchedulePaintInRect( |
566 gfx::Rect(0, 0, header_view_->width(), | 517 gfx::Rect(0, 0, header_view_->width(), |
567 std::max(top_left_height, top_right_height))); | 518 std::max(top_left_height, top_right_height))); |
568 } | 519 } |
569 | 520 |
570 gfx::Rect HeaderPainter::GetTitleBounds(const gfx::Font& title_font) { | 521 gfx::Rect HeaderPainter::GetTitleBounds(const gfx::Font& title_font) { |
571 int title_x = GetTitleOffsetX(); | 522 int title_x = GetTitleOffsetX(); |
572 // Center the text with respect to the caption button container. This way it | 523 // Center the text with respect to the caption button container. This way it |
573 // adapts to the caption button height and aligns exactly with the window | 524 // adapts to the caption button height and aligns exactly with the window |
574 // icon. Don't use |window_icon_| for this computation as it may be NULL. | 525 // icon. Don't use |window_icon_| for this computation as it may be NULL. |
575 int title_y = GetCaptionButtonContainerCenterY() - title_font.GetHeight() / 2; | 526 int title_y = GetCaptionButtonContainerCenterY() - title_font.GetHeight() / 2; |
576 return gfx::Rect( | 527 return gfx::Rect( |
577 title_x, | 528 title_x, |
578 std::max(0, title_y), | 529 std::max(0, title_y), |
579 std::max(0, caption_button_container_->x() - kTitleLogoSpacing - title_x), | 530 std::max(0, caption_button_container_->x() - kTitleLogoSpacing - title_x), |
580 title_font.GetHeight()); | 531 title_font.GetHeight()); |
581 } | 532 } |
582 | 533 |
583 } // namespace ash | 534 } // namespace ash |
OLD | NEW |