1 // Copyright (c) 2012 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 "ui/wm/core/shadow.h" 6 7 #include "third_party/skia/include/core/SkBitmap.h" 8 #include "ui/base/resource/resource_bundle.h" 9 #include "ui/compositor/layer.h" 10 #include "ui/compositor/scoped_layer_animation_settings.h" 11 #include "ui/resources/grit/ui_resources.h" 12 13 namespace { 14 15 // Shadow opacity for different styles. 16 const float kActiveShadowOpacity = 1.0f; 17 const float kInactiveShadowOpacity = 0.2f; 18 const float kSmallShadowOpacity = 1.0f; 19 20 // Shadow aperture for different styles. 21 // Note that this may be greater than interior inset to allow shadows with 22 // curved corners that extend inwards beyond a window's borders. 23 const int kActiveInteriorAperture = 134; 24 const int kInactiveInteriorAperture = 134; 25 const int kSmallInteriorAperture = 9; 26 27 // Interior inset for different styles. 28 const int kActiveInteriorInset = 64; 29 const int kInactiveInteriorInset = 64; 30 const int kSmallInteriorInset = 4; 31 32 // Duration for opacity animation in milliseconds. 33 const int kShadowAnimationDurationMs = 100; 34 35 float GetOpacityForStyle(wm::Shadow::Style style) { 36 switch (style) { 37 case wm::Shadow::STYLE_ACTIVE: 38 return kActiveShadowOpacity; 39 case wm::Shadow::STYLE_INACTIVE: 40 return kInactiveShadowOpacity; 41 case wm::Shadow::STYLE_SMALL: 42 return kSmallShadowOpacity; 43 } 44 return 1.0f; 45 } 46 47 int GetShadowApertureForStyle(wm::Shadow::Style style) { 48 switch (style) { 49 case wm::Shadow::STYLE_ACTIVE: 50 return kActiveInteriorAperture; 51 case wm::Shadow::STYLE_INACTIVE: 52 return kInactiveInteriorAperture; 53 case wm::Shadow::STYLE_SMALL: 54 return kSmallInteriorAperture; 55 } 56 return 0; 57 } 58 59 int GetInteriorInsetForStyle(wm::Shadow::Style style) { 60 switch (style) { 61 case wm::Shadow::STYLE_ACTIVE: 62 return kActiveInteriorInset; 63 case wm::Shadow::STYLE_INACTIVE: 64 return kInactiveInteriorInset; 65 case wm::Shadow::STYLE_SMALL: 66 return kSmallInteriorInset; 67 } 68 return 0; 69 } 70 71 } // namespace 72 73 namespace wm { 74 75 Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) { 76 } 77 78 Shadow::~Shadow() { 79 } 80 81 void Shadow::Init(Style style) { 82 style_ = style; 83 84 layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); 85 shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH)); 86 layer()->Add(shadow_layer_.get()); 87 88 UpdateImagesForStyle(); 89 shadow_layer_->set_name("Shadow"); 90 shadow_layer_->SetVisible(true); 91 shadow_layer_->SetFillsBoundsOpaquely(false); 92 shadow_layer_->SetOpacity(GetOpacityForStyle(style_)); 93 } 94 95 void Shadow::SetContentBounds(const gfx::Rect& content_bounds) { 96 content_bounds_ = content_bounds; 97 UpdateLayerBounds(); 98 } 99 100 void Shadow::SetStyle(Style style) { 101 if (style_ == style) 102 return; 103 104 Style old_style = style_; 105 style_ = style; 106 107 // Stop waiting for any as yet unfinished implicit animations. 108 StopObservingImplicitAnimations(); 109 110 // If we're switching to or from the small style, don't bother with 111 // animations. 112 if (style == STYLE_SMALL || old_style == STYLE_SMALL) { 113 UpdateImagesForStyle(); 114 shadow_layer_->SetOpacity(GetOpacityForStyle(style)); 115 return; 116 } 117 118 // If we're becoming active, switch images now. Because the inactive image 119 // has a very low opacity the switch isn't noticeable and this approach 120 // allows us to use only a single set of shadow images at a time. 121 if (style == STYLE_ACTIVE) { 122 UpdateImagesForStyle(); 123 // Opacity was baked into inactive image, start opacity low to match. 124 shadow_layer_->SetOpacity(kInactiveShadowOpacity); 125 } 126 127 { 128 // Property sets within this scope will be implicitly animated. 129 ui::ScopedLayerAnimationSettings settings(shadow_layer_->GetAnimator()); 130 settings.AddObserver(this); 131 settings.SetTransitionDuration( 132 base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs)); 133 switch (style_) { 134 case STYLE_ACTIVE: 135 shadow_layer_->SetOpacity(kActiveShadowOpacity); 136 break; 137 case STYLE_INACTIVE: 138 shadow_layer_->SetOpacity(kInactiveShadowOpacity); 139 break; 140 default: 141 NOTREACHED() << "Unhandled style " << style_; 142 break; 143 } 144 } 145 } 146 147 void Shadow::OnImplicitAnimationsCompleted() { 148 // If we just finished going inactive, switch images. This doesn't cause 149 // a visual pop because the inactive image opacity is so low. 150 if (style_ == STYLE_INACTIVE) { 151 UpdateImagesForStyle(); 152 // Opacity is baked into inactive image, so set fully opaque. 153 shadow_layer_->SetOpacity(1.0f); 154 } 155 } 156 157 void Shadow::UpdateImagesForStyle() { 158 ResourceBundle& res = ResourceBundle::GetSharedInstance(); 159 gfx::Image image; 160 switch (style_) { 161 case STYLE_ACTIVE: 162 image = res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE); 163 break; 164 case STYLE_INACTIVE: 165 image = res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE); 166 break; 167 case STYLE_SMALL: 168 image = res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL); 169 break; 170 default: 171 NOTREACHED() << "Unhandled style " << style_; 172 break; 173 } 174 175 shadow_layer_->UpdateNinePatchLayerBitmap(image.AsBitmap()); 176 image_size_ = image.Size(); 177 interior_inset_ = GetInteriorInsetForStyle(style_); 178 179 // Image sizes may have changed. 180 UpdateLayerBounds(); 181 } 182 183 void Shadow::UpdateLayerBounds() { 184 // Update bounds based on content bounds and interior inset. 185 gfx::Rect layer_bounds = content_bounds_; 186 layer_bounds.Inset(-interior_inset_, -interior_inset_); 187 layer()->SetBounds(layer_bounds); 188 shadow_layer_->SetBounds(gfx::Rect(layer_bounds.size())); 189 190 // Update the shadow aperture and border for style. Note that border is in 191 // layer space and it cannot exceed the bounds of the layer. 192 int aperture = GetShadowApertureForStyle(style_); 193 int aperture_x = std::min(aperture, layer_bounds.width() / 2); 194 int aperture_y = std::min(aperture, layer_bounds.height() / 2); 195 shadow_layer_->UpdateNinePatchLayerAperture( 196 gfx::Rect(aperture_x, aperture_y, 197 image_size_.width() - aperture_x * 2, 198 image_size_.height() - aperture_y * 2)); 199 shadow_layer_->UpdateNinePatchLayerBorder( 200 gfx::Rect(aperture_x, aperture_y, aperture_x * 2, aperture_y * 2)); 201 } 202 203 } // namespace wm 204