#include "ui/compositor_extra/shadow.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/shadow_util.h"
namespace ui {
namespace {
constexpr int kShadowAnimationDurationMs = 100;
}
Shadow::Shadow() : shadow_layer_owner_(this) {}
Shadow::~Shadow() = default;
void Shadow::Init(int elevation) {
DCHECK_GE(elevation, 0);
desired_elevation_ = elevation;
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_NOT_DRAWN));
layer()->SetName("Shadow Parent Container");
RecreateShadowLayer();
}
void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
if (content_bounds == content_bounds_)
return;
content_bounds_ = content_bounds;
UpdateLayerBounds();
}
void Shadow::SetElevation(int elevation) {
DCHECK_GE(elevation, 0);
if (desired_elevation_ == elevation)
return;
desired_elevation_ = elevation;
StopObservingImplicitAnimations();
DCHECK(shadow_layer());
fading_layer_owner_.Reset(shadow_layer_owner_.ReleaseLayer());
RecreateShadowLayer();
shadow_layer()->SetOpacity(0.f);
{
ui::ScopedLayerAnimationSettings settings(fading_layer()->GetAnimator());
settings.AddObserver(this);
settings.SetTransitionDuration(
base::Milliseconds(kShadowAnimationDurationMs));
fading_layer()->SetOpacity(0.f);
}
{
ui::ScopedLayerAnimationSettings settings(shadow_layer()->GetAnimator());
settings.SetTransitionDuration(
base::Milliseconds(kShadowAnimationDurationMs));
shadow_layer()->SetOpacity(1.f);
}
}
void Shadow::SetRoundedCornerRadius(int rounded_corner_radius) {
DCHECK_GE(rounded_corner_radius, 0);
if (rounded_corner_radius_ == rounded_corner_radius)
return;
rounded_corner_radius_ = rounded_corner_radius;
UpdateLayerBounds();
}
void Shadow::SetShadowStyle(gfx::ShadowStyle style) {
if (style_ == style)
return;
style_ = style;
RecreateShadowLayer();
}
void Shadow::OnImplicitAnimationsCompleted() {
std::unique_ptr<ui::Layer> to_be_deleted = fading_layer_owner_.ReleaseLayer();
UpdateLayerBounds();
}
Shadow::ShadowLayerOwner::ShadowLayerOwner(Shadow* owner,
std::unique_ptr<Layer> layer)
: LayerOwner(std::move(layer)), owner_shadow_(owner) {}
Shadow::ShadowLayerOwner::~ShadowLayerOwner() = default;
std::unique_ptr<Layer> Shadow::ShadowLayerOwner::RecreateLayer() {
auto result = ui::LayerOwner::RecreateLayer();
owner_shadow_->details_ = nullptr;
owner_shadow_->UpdateLayerBounds();
return result;
}
void Shadow::RecreateShadowLayer() {
shadow_layer_owner_.Reset(std::make_unique<ui::Layer>(ui::LAYER_NINE_PATCH));
shadow_layer()->SetName("Shadow");
shadow_layer()->SetVisible(true);
shadow_layer()->SetFillsBoundsOpaquely(false);
layer()->Add(shadow_layer());
UpdateLayerBounds();
}
void Shadow::UpdateLayerBounds() {
if (content_bounds_.IsEmpty())
return;
const int smaller_dimension =
std::min(content_bounds_.width(), content_bounds_.height());
const int size_adjusted_elevation =
std::min((smaller_dimension - 2 * rounded_corner_radius_) / 4,
static_cast<int>(desired_elevation_));
const auto& details = gfx::ShadowDetails::Get(size_adjusted_elevation,
rounded_corner_radius_, style_);
gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) +
gfx::Insets(rounded_corner_radius_);
if ((&details != details_) && (details_ || size_adjusted_elevation)) {
shadow_layer()->UpdateNinePatchLayerImage(details.ninebox_image);
gfx::Rect aperture(details.ninebox_image.size());
aperture.Inset(blur_region);
shadow_layer()->UpdateNinePatchLayerAperture(aperture);
details_ = &details;
}
const gfx::Insets margins = gfx::ShadowValue::GetMargin(details.values);
gfx::Rect new_layer_bounds = content_bounds_;
new_layer_bounds.Inset(margins);
gfx::Rect shadow_layer_bounds(new_layer_bounds.size());
if (fading_layer()) {
const gfx::Rect old_layer_bounds = layer()->bounds();
gfx::Rect combined_layer_bounds = old_layer_bounds;
combined_layer_bounds.Union(new_layer_bounds);
layer()->SetBounds(combined_layer_bounds);
gfx::Rect fading_layer_bounds(fading_layer()->bounds());
fading_layer_bounds.Offset(old_layer_bounds.origin() -
combined_layer_bounds.origin());
fading_layer()->SetBounds(fading_layer_bounds);
shadow_layer_bounds.Offset(new_layer_bounds.origin() -
combined_layer_bounds.origin());
} else {
layer()->SetBounds(new_layer_bounds);
}
shadow_layer()->SetBounds(shadow_layer_bounds);
gfx::Rect occlusion_bounds(shadow_layer_bounds.size());
occlusion_bounds.Inset(-margins + gfx::Insets(rounded_corner_radius_));
shadow_layer()->UpdateNinePatchOcclusion(occlusion_bounds);
shadow_layer()->UpdateNinePatchLayerBorder(
gfx::Rect(blur_region.left(), blur_region.top(), blur_region.width(),
blur_region.height()));
}
}