| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "ui/wm/core/shadow.h" | 5 #include "ui/wm/core/shadow.h" |
| 6 | 6 |
| 7 #include "base/lazy_instance.h" | 7 #include "base/lazy_instance.h" |
| 8 #include "ui/base/resource/resource_bundle.h" | 8 #include "ui/base/resource/resource_bundle.h" |
| 9 #include "ui/compositor/layer.h" | 9 #include "ui/compositor/layer.h" |
| 10 #include "ui/compositor/scoped_layer_animation_settings.h" | 10 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 11 #include "ui/gfx/geometry/insets.h" | 11 #include "ui/gfx/geometry/insets.h" |
| 12 #include "ui/gfx/image/image_skia_operations.h" | 12 #include "ui/gfx/image/image_skia_operations.h" |
| 13 | 13 |
| 14 namespace wm { | 14 namespace wm { |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 // The opacity used for active shadow when animating between | |
| 19 // inactive/active shadow. | |
| 20 const float kInactiveShadowAnimationOpacity = 0.2f; | |
| 21 | |
| 22 // Rounded corners are overdrawn on top of the window's content layer, | 18 // Rounded corners are overdrawn on top of the window's content layer, |
| 23 // we need to exclude them from the occlusion area. | 19 // we need to exclude them from the occlusion area. |
| 24 const int kRoundedCornerRadius = 2; | 20 const int kRoundedCornerRadius = 2; |
| 25 | 21 |
| 26 // Duration for opacity animation in milliseconds. | 22 // Duration for opacity animation in milliseconds. |
| 27 const int kShadowAnimationDurationMs = 100; | 23 const int kShadowAnimationDurationMs = 100; |
| 28 | 24 |
| 29 struct ShadowDetails { | 25 struct ShadowDetails { |
| 30 // Description of the shadows. | 26 // Description of the shadows. |
| 31 gfx::ShadowValues values; | 27 gfx::ShadowValues values; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 } | 62 } |
| 67 | 63 |
| 68 } // namespace | 64 } // namespace |
| 69 | 65 |
| 70 Shadow::Shadow() {} | 66 Shadow::Shadow() {} |
| 71 | 67 |
| 72 Shadow::~Shadow() {} | 68 Shadow::~Shadow() {} |
| 73 | 69 |
| 74 void Shadow::Init(Style style) { | 70 void Shadow::Init(Style style) { |
| 75 style_ = style; | 71 style_ = style; |
| 76 | |
| 77 layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); | 72 layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); |
| 78 shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH)); | 73 RecreateShadowLayer(); |
| 79 layer()->Add(shadow_layer_.get()); | |
| 80 | |
| 81 UpdateImagesForStyle(); | |
| 82 shadow_layer_->set_name("Shadow"); | |
| 83 shadow_layer_->SetVisible(true); | |
| 84 shadow_layer_->SetFillsBoundsOpaquely(false); | |
| 85 } | 74 } |
| 86 | 75 |
| 87 void Shadow::SetContentBounds(const gfx::Rect& content_bounds) { | 76 void Shadow::SetContentBounds(const gfx::Rect& content_bounds) { |
| 77 // When the window moves but doesn't change size, this is a no-op. (The |
| 78 // origin stays the same in this case.) |
| 79 if (content_bounds == content_bounds_) |
| 80 return; |
| 81 |
| 88 content_bounds_ = content_bounds; | 82 content_bounds_ = content_bounds; |
| 89 UpdateLayerBounds(); | 83 UpdateLayerBounds(); |
| 90 } | 84 } |
| 91 | 85 |
| 92 void Shadow::SetStyle(Style style) { | 86 void Shadow::SetStyle(Style style) { |
| 93 if (style_ == style) | 87 if (style_ == style) |
| 94 return; | 88 return; |
| 95 | 89 |
| 96 Style old_style = style_; | |
| 97 style_ = style; | 90 style_ = style; |
| 98 | 91 |
| 99 // Stop waiting for any as yet unfinished implicit animations. | 92 // Stop waiting for any as yet unfinished implicit animations. |
| 100 StopObservingImplicitAnimations(); | 93 StopObservingImplicitAnimations(); |
| 101 | 94 |
| 102 // If we're switching to or from the small style, don't bother with | 95 // The old shadow layer is the new fading out layer. |
| 103 // animations. | 96 DCHECK(shadow_layer_); |
| 104 if (style == STYLE_SMALL || old_style == STYLE_SMALL) { | 97 fading_layer_ = std::move(shadow_layer_); |
| 105 UpdateImagesForStyle(); | 98 RecreateShadowLayer(); |
| 106 // Make sure the shadow is fully opaque. | 99 shadow_layer_->SetOpacity(0.f); |
| 107 shadow_layer_->SetOpacity(1.0f); | |
| 108 return; | |
| 109 } | |
| 110 | 100 |
| 111 // If we're becoming active, switch images now. Because the inactive image | 101 { |
| 112 // has a very low opacity the switch isn't noticeable and this approach | 102 // Observe the fade out animation so we can clean up the layer when done. |
| 113 // allows us to use only a single set of shadow images at a time. | 103 ui::ScopedLayerAnimationSettings settings(fading_layer_->GetAnimator()); |
| 114 if (style == STYLE_ACTIVE) { | 104 settings.AddObserver(this); |
| 115 UpdateImagesForStyle(); | 105 settings.SetTransitionDuration( |
| 116 // Opacity was baked into inactive image, start opacity low to match. | 106 base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs)); |
| 117 shadow_layer_->SetOpacity(kInactiveShadowAnimationOpacity); | 107 fading_layer_->SetOpacity(0.f); |
| 118 } | 108 } |
| 119 | 109 |
| 120 { | 110 { |
| 121 // Property sets within this scope will be implicitly animated. | 111 // We don't care to observe this one. |
| 122 ui::ScopedLayerAnimationSettings settings(shadow_layer_->GetAnimator()); | 112 ui::ScopedLayerAnimationSettings settings(shadow_layer_->GetAnimator()); |
| 123 settings.AddObserver(this); | |
| 124 settings.SetTransitionDuration( | 113 settings.SetTransitionDuration( |
| 125 base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs)); | 114 base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs)); |
| 126 switch (style_) { | 115 shadow_layer_->SetOpacity(1.f); |
| 127 case STYLE_ACTIVE: | |
| 128 // Animate the active shadow from kInactiveShadowAnimationOpacity to | |
| 129 // 1.0f. | |
| 130 shadow_layer_->SetOpacity(1.0f); | |
| 131 break; | |
| 132 case STYLE_INACTIVE: | |
| 133 // The opacity will be reset to 1.0f when animation is completed. | |
| 134 shadow_layer_->SetOpacity(kInactiveShadowAnimationOpacity); | |
| 135 break; | |
| 136 default: | |
| 137 NOTREACHED() << "Unhandled style " << style_; | |
| 138 break; | |
| 139 } | |
| 140 } | 116 } |
| 141 } | 117 } |
| 142 | 118 |
| 143 void Shadow::OnImplicitAnimationsCompleted() { | 119 void Shadow::OnImplicitAnimationsCompleted() { |
| 144 // If we just finished going inactive, switch images. This doesn't cause | 120 fading_layer_.reset(); |
| 145 // a visual pop because the inactive image opacity is so low. | 121 // The size needed for layer() may be smaller now that |fading_layer_| is |
| 146 if (style_ == STYLE_INACTIVE) { | 122 // removed. |
| 147 UpdateImagesForStyle(); | 123 UpdateLayerBounds(); |
| 148 // Opacity is baked into inactive image, so set fully opaque. | |
| 149 shadow_layer_->SetOpacity(1.0f); | |
| 150 } | |
| 151 } | 124 } |
| 152 | 125 |
| 153 void Shadow::UpdateImagesForStyle() { | 126 void Shadow::RecreateShadowLayer() { |
| 127 shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH)); |
| 128 shadow_layer_->set_name("Shadow"); |
| 129 shadow_layer_->SetVisible(true); |
| 130 shadow_layer_->SetFillsBoundsOpaquely(false); |
| 131 layer()->Add(shadow_layer_.get()); |
| 132 |
| 154 const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle()); | 133 const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle()); |
| 155 shadow_layer_->UpdateNinePatchLayerImage(details.ninebox_image); | 134 shadow_layer_->UpdateNinePatchLayerImage(details.ninebox_image); |
| 156 // The ninebox grid is defined in terms of the image size. The shadow blurs in | 135 // The ninebox grid is defined in terms of the image size. The shadow blurs in |
| 157 // both inward and outward directions from the edge of the contents, so the | 136 // both inward and outward directions from the edge of the contents, so the |
| 158 // aperture goes further inside the image than the shadow margins (which | 137 // aperture goes further inside the image than the shadow margins (which |
| 159 // represent exterior blur). | 138 // represent exterior blur). |
| 160 gfx::Rect aperture(details.ninebox_image.size()); | 139 gfx::Rect aperture(details.ninebox_image.size()); |
| 161 gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) + | 140 gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) + |
| 162 gfx::Insets(kRoundedCornerRadius); | 141 gfx::Insets(kRoundedCornerRadius); |
| 163 aperture.Inset(blur_region); | 142 aperture.Inset(blur_region); |
| 164 shadow_layer_->UpdateNinePatchLayerAperture(aperture); | 143 shadow_layer_->UpdateNinePatchLayerAperture(aperture); |
| 165 UpdateLayerBounds(); | 144 UpdateLayerBounds(); |
| 166 } | 145 } |
| 167 | 146 |
| 168 void Shadow::UpdateLayerBounds() { | 147 void Shadow::UpdateLayerBounds() { |
| 169 const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle()); | 148 const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle()); |
| 170 // Shadow margins are negative, so this expands outwards from | 149 // Shadow margins are negative, so this expands outwards from |
| 171 // |content_bounds_|. | 150 // |content_bounds_|. |
| 172 const gfx::Insets margins = gfx::ShadowValue::GetMargin(details.values); | 151 const gfx::Insets margins = gfx::ShadowValue::GetMargin(details.values); |
| 173 gfx::Rect layer_bounds = content_bounds_; | 152 gfx::Rect new_layer_bounds = content_bounds_; |
| 174 layer_bounds.Inset(margins); | 153 new_layer_bounds.Inset(margins); |
| 175 layer()->SetBounds(layer_bounds); | 154 gfx::Rect shadow_layer_bounds(new_layer_bounds.size()); |
| 176 const gfx::Rect shadow_layer_bounds(layer_bounds.size()); | 155 |
| 156 // When there's an old shadow fading out, the bounds of layer() have to be |
| 157 // big enough to encompass both shadows. |
| 158 if (fading_layer_) { |
| 159 const gfx::Rect old_layer_bounds = layer()->bounds(); |
| 160 gfx::Rect combined_layer_bounds = old_layer_bounds; |
| 161 combined_layer_bounds.Union(new_layer_bounds); |
| 162 layer()->SetBounds(combined_layer_bounds); |
| 163 |
| 164 // If this is reached via SetContentBounds, we might hypothetically need |
| 165 // to change the size of the fading layer, but the fade is so fast it's |
| 166 // not really an issue. |
| 167 gfx::Rect fading_layer_bounds(fading_layer_->bounds()); |
| 168 fading_layer_bounds.Offset(old_layer_bounds.origin() - |
| 169 combined_layer_bounds.origin()); |
| 170 fading_layer_->SetBounds(fading_layer_bounds); |
| 171 |
| 172 shadow_layer_bounds.Offset(new_layer_bounds.origin() - |
| 173 combined_layer_bounds.origin()); |
| 174 } else { |
| 175 layer()->SetBounds(new_layer_bounds); |
| 176 } |
| 177 |
| 177 shadow_layer_->SetBounds(shadow_layer_bounds); | 178 shadow_layer_->SetBounds(shadow_layer_bounds); |
| 178 | 179 |
| 179 // Occlude the region inside the bounding box. Occlusion uses shadow layer | 180 // Occlude the region inside the bounding box. Occlusion uses shadow layer |
| 180 // space. See nine_patch_layer.h for more context on what's going on here. | 181 // space. See nine_patch_layer.h for more context on what's going on here. |
| 181 gfx::Rect occlusion_bounds = shadow_layer_bounds; | 182 gfx::Rect occlusion_bounds(shadow_layer_bounds.size()); |
| 182 occlusion_bounds.Inset(-margins + gfx::Insets(kRoundedCornerRadius)); | 183 occlusion_bounds.Inset(-margins + gfx::Insets(kRoundedCornerRadius)); |
| 183 shadow_layer_->UpdateNinePatchOcclusion(occlusion_bounds); | 184 shadow_layer_->UpdateNinePatchOcclusion(occlusion_bounds); |
| 184 | 185 |
| 185 // The border is more or less the same inset as the aperture, but can be no | 186 // The border is more or less the same inset as the aperture, but can be no |
| 186 // larger than the shadow layer. When the shadow layer is too small, shrink | 187 // larger than the shadow layer. When the shadow layer is too small, shrink |
| 187 // the dimensions proportionally. | 188 // the dimensions proportionally. |
| 188 gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) + | 189 gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) + |
| 189 gfx::Insets(kRoundedCornerRadius); | 190 gfx::Insets(kRoundedCornerRadius); |
| 190 int border_w = std::min(blur_region.width(), shadow_layer_bounds.width()); | 191 int border_w = std::min(blur_region.width(), shadow_layer_bounds.width()); |
| 191 int border_x = border_w * blur_region.left() / blur_region.width(); | 192 int border_x = border_w * blur_region.left() / blur_region.width(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 202 case STYLE_INACTIVE: | 203 case STYLE_INACTIVE: |
| 203 return 8; | 204 return 8; |
| 204 case STYLE_SMALL: | 205 case STYLE_SMALL: |
| 205 return 6; | 206 return 6; |
| 206 } | 207 } |
| 207 NOTREACHED(); | 208 NOTREACHED(); |
| 208 return 0; | 209 return 0; |
| 209 } | 210 } |
| 210 | 211 |
| 211 } // namespace wm | 212 } // namespace wm |
| OLD | NEW |