15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/wm/core/shadow.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "third_party/skia/include/core/SkBitmap.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
9116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/compositor/layer.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h"
1103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ui/resources/grit/ui_resources.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Shadow opacity for different styles.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const float kActiveShadowOpacity = 1.0f;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const float kInactiveShadowOpacity = 0.2f;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const float kSmallShadowOpacity = 1.0f;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Shadow aperture for different styles.
21116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Note that this may be greater than interior inset to allow shadows with
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// curved corners that extend inwards beyond a window's borders.
23116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kActiveInteriorAperture = 134;
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kInactiveInteriorAperture = 134;
25116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kSmallInteriorAperture = 9;
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Interior inset for different styles.
28116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kActiveInteriorInset = 64;
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kInactiveInteriorInset = 64;
30116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst int kSmallInteriorInset = 4;
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Duration for opacity animation in milliseconds.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kShadowAnimationDurationMs = 100;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)float GetOpacityForStyle(wm::Shadow::Style style) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (style) {
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_ACTIVE:
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return kActiveShadowOpacity;
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_INACTIVE:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return kInactiveShadowOpacity;
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_SMALL:
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return kSmallShadowOpacity;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1.0f;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47116680a4aac90f2aa7413d9095a592090648e557Ben Murdochint GetShadowApertureForStyle(wm::Shadow::Style style) {
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  switch (style) {
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case wm::Shadow::STYLE_ACTIVE:
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return kActiveInteriorAperture;
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case wm::Shadow::STYLE_INACTIVE:
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return kInactiveInteriorAperture;
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case wm::Shadow::STYLE_SMALL:
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return kSmallInteriorAperture;
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return 0;
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)int GetInteriorInsetForStyle(wm::Shadow::Style style) {
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  switch (style) {
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_ACTIVE:
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return kActiveInteriorInset;
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_INACTIVE:
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return kInactiveInteriorInset;
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case wm::Shadow::STYLE_SMALL:
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return kSmallInteriorInset;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return 0;
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace wm {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Shadow::~Shadow() {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Shadow::Init(Style style) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  style_ = style;
83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH));
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  layer()->Add(shadow_layer_.get());
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateImagesForStyle();
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_->set_name("Shadow");
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_->SetVisible(true);
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_->SetFillsBoundsOpaquely(false);
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_->SetOpacity(GetOpacityForStyle(style_));
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_bounds_ = content_bounds;
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UpdateLayerBounds();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Shadow::SetStyle(Style style) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ == style)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Style old_style = style_;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  style_ = style;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop waiting for any as yet unfinished implicit animations.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopObservingImplicitAnimations();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're switching to or from the small style, don't bother with
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // animations.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style == STYLE_SMALL || old_style == STYLE_SMALL) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateImagesForStyle();
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    shadow_layer_->SetOpacity(GetOpacityForStyle(style));
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're becoming active, switch images now.  Because the inactive image
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // has a very low opacity the switch isn't noticeable and this approach
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // allows us to use only a single set of shadow images at a time.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style == STYLE_ACTIVE) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateImagesForStyle();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Opacity was baked into inactive image, start opacity low to match.
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    shadow_layer_->SetOpacity(kInactiveShadowOpacity);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Property sets within this scope will be implicitly animated.
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ui::ScopedLayerAnimationSettings settings(shadow_layer_->GetAnimator());
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    settings.AddObserver(this);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    settings.SetTransitionDuration(
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs));
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (style_) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STYLE_ACTIVE:
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        shadow_layer_->SetOpacity(kActiveShadowOpacity);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STYLE_INACTIVE:
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        shadow_layer_->SetOpacity(kInactiveShadowOpacity);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED() << "Unhandled style " << style_;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Shadow::OnImplicitAnimationsCompleted() {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we just finished going inactive, switch images.  This doesn't cause
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a visual pop because the inactive image opacity is so low.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ == STYLE_INACTIVE) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateImagesForStyle();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Opacity is baked into inactive image, so set fully opaque.
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    shadow_layer_->SetOpacity(1.0f);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Shadow::UpdateImagesForStyle() {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResourceBundle& res = ResourceBundle::GetSharedInstance();
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  gfx::Image image;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (style_) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case STYLE_ACTIVE:
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      image = res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case STYLE_INACTIVE:
165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      image = res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case STYLE_SMALL:
168116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      image = res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "Unhandled style " << style_;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  shadow_layer_->UpdateNinePatchLayerBitmap(image.AsBitmap());
1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  image_size_ = image.Size();
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  interior_inset_ = GetInteriorInsetForStyle(style_);
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Image sizes may have changed.
180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UpdateLayerBounds();
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid Shadow::UpdateLayerBounds() {
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Update bounds based on content bounds and interior inset.
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  gfx::Rect layer_bounds = content_bounds_;
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  layer_bounds.Inset(-interior_inset_, -interior_inset_);
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  layer()->SetBounds(layer_bounds);
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  shadow_layer_->SetBounds(gfx::Rect(layer_bounds.size()));
189116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Update the shadow aperture and border for style. Note that border is in
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // layer space and it cannot exceed the bounds of the layer.
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int aperture = GetShadowApertureForStyle(style_);
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int aperture_x = std::min(aperture, layer_bounds.width() / 2);
1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int aperture_y = std::min(aperture, layer_bounds.height() / 2);
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  shadow_layer_->UpdateNinePatchLayerAperture(
1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gfx::Rect(aperture_x, aperture_y,
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                image_size_.width() - aperture_x * 2,
1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                image_size_.height() - aperture_y * 2));
1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  shadow_layer_->UpdateNinePatchLayerBorder(
2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gfx::Rect(aperture_x, aperture_y, aperture_x * 2, aperture_y * 2));
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace wm
204