shadow.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. #include "ui/compositor_extra/shadow.h"
  5. #include "ui/base/resource/resource_bundle.h"
  6. #include "ui/compositor/layer.h"
  7. #include "ui/compositor/scoped_layer_animation_settings.h"
  8. #include "ui/gfx/geometry/insets.h"
  9. #include "ui/gfx/shadow_util.h"
  10. namespace ui {
  11. namespace {
  12. // Duration for opacity animation in milliseconds.
  13. constexpr int kShadowAnimationDurationMs = 100;
  14. } // namespace
  15. Shadow::Shadow() : shadow_layer_owner_(this) {}
  16. Shadow::~Shadow() = default;
  17. void Shadow::Init(int elevation) {
  18. DCHECK_GE(elevation, 0);
  19. desired_elevation_ = elevation;
  20. SetLayer(std::make_unique<ui::Layer>(ui::LAYER_NOT_DRAWN));
  21. layer()->SetName("Shadow Parent Container");
  22. RecreateShadowLayer();
  23. }
  24. void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
  25. // When the window moves but doesn't change size, this is a no-op. (The
  26. // origin stays the same in this case.)
  27. if (content_bounds == content_bounds_)
  28. return;
  29. content_bounds_ = content_bounds;
  30. UpdateLayerBounds();
  31. }
  32. void Shadow::SetElevation(int elevation) {
  33. DCHECK_GE(elevation, 0);
  34. if (desired_elevation_ == elevation)
  35. return;
  36. desired_elevation_ = elevation;
  37. // Stop waiting for any as yet unfinished implicit animations.
  38. StopObservingImplicitAnimations();
  39. // The old shadow layer is the new fading out layer.
  40. DCHECK(shadow_layer());
  41. fading_layer_owner_.Reset(shadow_layer_owner_.ReleaseLayer());
  42. RecreateShadowLayer();
  43. shadow_layer()->SetOpacity(0.f);
  44. {
  45. // Observe the fade out animation so we can clean up the layer when done.
  46. ui::ScopedLayerAnimationSettings settings(fading_layer()->GetAnimator());
  47. settings.AddObserver(this);
  48. settings.SetTransitionDuration(
  49. base::Milliseconds(kShadowAnimationDurationMs));
  50. fading_layer()->SetOpacity(0.f);
  51. }
  52. {
  53. // We don't care to observe this one.
  54. ui::ScopedLayerAnimationSettings settings(shadow_layer()->GetAnimator());
  55. settings.SetTransitionDuration(
  56. base::Milliseconds(kShadowAnimationDurationMs));
  57. shadow_layer()->SetOpacity(1.f);
  58. }
  59. }
  60. void Shadow::SetRoundedCornerRadius(int rounded_corner_radius) {
  61. DCHECK_GE(rounded_corner_radius, 0);
  62. if (rounded_corner_radius_ == rounded_corner_radius)
  63. return;
  64. rounded_corner_radius_ = rounded_corner_radius;
  65. UpdateLayerBounds();
  66. }
  67. void Shadow::SetShadowStyle(gfx::ShadowStyle style) {
  68. if (style_ == style)
  69. return;
  70. style_ = style;
  71. RecreateShadowLayer();
  72. }
  73. void Shadow::OnImplicitAnimationsCompleted() {
  74. std::unique_ptr<ui::Layer> to_be_deleted = fading_layer_owner_.ReleaseLayer();
  75. // The size needed for layer() may be smaller now that |fading_layer()| is
  76. // removed.
  77. UpdateLayerBounds();
  78. }
  79. // -----------------------------------------------------------------------------
  80. // Shadow::ShadowLayerOwner:
  81. Shadow::ShadowLayerOwner::ShadowLayerOwner(Shadow* owner,
  82. std::unique_ptr<Layer> layer)
  83. : LayerOwner(std::move(layer)), owner_shadow_(owner) {}
  84. Shadow::ShadowLayerOwner::~ShadowLayerOwner() = default;
  85. std::unique_ptr<Layer> Shadow::ShadowLayerOwner::RecreateLayer() {
  86. auto result = ui::LayerOwner::RecreateLayer();
  87. // Now update the newly recreated shadow layer with the correct nine patch
  88. // image details.
  89. owner_shadow_->details_ = nullptr;
  90. owner_shadow_->UpdateLayerBounds();
  91. return result;
  92. }
  93. // -----------------------------------------------------------------------------
  94. // Shadow:
  95. void Shadow::RecreateShadowLayer() {
  96. shadow_layer_owner_.Reset(std::make_unique<ui::Layer>(ui::LAYER_NINE_PATCH));
  97. shadow_layer()->SetName("Shadow");
  98. shadow_layer()->SetVisible(true);
  99. shadow_layer()->SetFillsBoundsOpaquely(false);
  100. layer()->Add(shadow_layer());
  101. UpdateLayerBounds();
  102. }
  103. void Shadow::UpdateLayerBounds() {
  104. if (content_bounds_.IsEmpty())
  105. return;
  106. // The ninebox assumption breaks down when the window is too small for the
  107. // desired elevation. The height/width of |blur_region| will be 4 * elevation
  108. // (see ShadowDetails::Get), so cap elevation at the most we can handle.
  109. const int smaller_dimension =
  110. std::min(content_bounds_.width(), content_bounds_.height());
  111. const int size_adjusted_elevation =
  112. std::min((smaller_dimension - 2 * rounded_corner_radius_) / 4,
  113. static_cast<int>(desired_elevation_));
  114. const auto& details = gfx::ShadowDetails::Get(size_adjusted_elevation,
  115. rounded_corner_radius_, style_);
  116. gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) +
  117. gfx::Insets(rounded_corner_radius_);
  118. // Update |shadow_layer()| if details changed and it has been updated in
  119. // the past (|details_| is set), or elevation is non-zero.
  120. if ((&details != details_) && (details_ || size_adjusted_elevation)) {
  121. shadow_layer()->UpdateNinePatchLayerImage(details.ninebox_image);
  122. // The ninebox grid is defined in terms of the image size. The shadow blurs
  123. // in both inward and outward directions from the edge of the contents, so
  124. // the aperture goes further inside the image than the shadow margins (which
  125. // represent exterior blur).
  126. gfx::Rect aperture(details.ninebox_image.size());
  127. aperture.Inset(blur_region);
  128. shadow_layer()->UpdateNinePatchLayerAperture(aperture);
  129. details_ = &details;
  130. }
  131. // Shadow margins are negative, so this expands outwards from
  132. // |content_bounds_|.
  133. const gfx::Insets margins = gfx::ShadowValue::GetMargin(details.values);
  134. gfx::Rect new_layer_bounds = content_bounds_;
  135. new_layer_bounds.Inset(margins);
  136. gfx::Rect shadow_layer_bounds(new_layer_bounds.size());
  137. // When there's an old shadow fading out, the bounds of layer() have to be
  138. // big enough to encompass both shadows.
  139. if (fading_layer()) {
  140. const gfx::Rect old_layer_bounds = layer()->bounds();
  141. gfx::Rect combined_layer_bounds = old_layer_bounds;
  142. combined_layer_bounds.Union(new_layer_bounds);
  143. layer()->SetBounds(combined_layer_bounds);
  144. // If this is reached via SetContentBounds, we might hypothetically need
  145. // to change the size of the fading layer, but the fade is so fast it's
  146. // not really an issue.
  147. gfx::Rect fading_layer_bounds(fading_layer()->bounds());
  148. fading_layer_bounds.Offset(old_layer_bounds.origin() -
  149. combined_layer_bounds.origin());
  150. fading_layer()->SetBounds(fading_layer_bounds);
  151. shadow_layer_bounds.Offset(new_layer_bounds.origin() -
  152. combined_layer_bounds.origin());
  153. } else {
  154. layer()->SetBounds(new_layer_bounds);
  155. }
  156. shadow_layer()->SetBounds(shadow_layer_bounds);
  157. // Occlude the region inside the bounding box. Occlusion uses shadow layer
  158. // space. See nine_patch_layer.h for more context on what's going on here.
  159. gfx::Rect occlusion_bounds(shadow_layer_bounds.size());
  160. occlusion_bounds.Inset(-margins + gfx::Insets(rounded_corner_radius_));
  161. shadow_layer()->UpdateNinePatchOcclusion(occlusion_bounds);
  162. // The border is the same inset as the aperture.
  163. shadow_layer()->UpdateNinePatchLayerBorder(
  164. gfx::Rect(blur_region.left(), blur_region.top(), blur_region.width(),
  165. blur_region.height()));
  166. }
  167. } // namespace ui