chromium/ash/style/blurred_background_shield.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/style/blurred_background_shield.h"

#include "ash/public/cpp/style/color_provider.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/views/view.h"

namespace ash {

BlurredBackgroundShield::BlurredBackgroundShield(
    views::View* host,
    absl::variant<SkColor, ui::ColorId> color,
    float blur_sigma,
    const gfx::RoundedCornersF& rounded_corners,
    bool add_layer_to_region)
    : host_(host),
      color_(color),
      blur_sigma_(blur_sigma),
      add_layer_to_region_(add_layer_to_region) {
  host_observation_.Observe(host_);

  if (add_layer_to_region_) {
    // `AddLayerToRegion` adds the background layer as a child layer of the host
    // view's parent layer. The background layer will be positioned below the
    // host view and synchronize the the location and visibility with the host
    // view.
    background_layer_.SetBounds(gfx::Rect(host_->size()));
    host_->AddLayerToRegion(&background_layer_, views::LayerRegion::kBelow);
  } else {
    // If the layer is not added to the below region of the host, the host view
    // should owns a layer for ease of layer hierarchy arrangement. The
    // background layer should be stacked below the host layer manually.
    CHECK(host_->layer());
    if (host_->layer()->parent()) {
      StackLayerBelowHost();
    }
  }

  background_layer_.SetRoundedCornerRadius(rounded_corners);
  UpdateBackgroundColor();
}

BlurredBackgroundShield::~BlurredBackgroundShield() {
  if (add_layer_to_region_) {
    host_->RemoveLayerFromRegions(&background_layer_);
  }
}

void BlurredBackgroundShield::SetColor(SkColor color) {
  if (absl::holds_alternative<SkColor>(color_) &&
      absl::get<SkColor>(color_) == color) {
    return;
  }

  color_ = color;
  UpdateBackgroundColor();
}

void BlurredBackgroundShield::SetColorId(ui::ColorId color_id) {
  if (absl::holds_alternative<ui::ColorId>(color_) &&
      absl::get<ui::ColorId>(color_) == color_id) {
    return;
  }

  color_ = color_id;
  UpdateBackgroundColor();
}

void BlurredBackgroundShield::OnViewAddedToWidget(views::View* observed_view) {
  if (!add_layer_to_region_) {
    StackLayerBelowHost();
  }
}

void BlurredBackgroundShield::OnViewVisibilityChanged(
    views::View* observed_view,
    views::View* starting_view) {
  background_layer_.SetVisible(host_->GetVisible());
}

void BlurredBackgroundShield::OnViewLayerBoundsSet(views::View* observed_view) {
  if (auto* host_layer = host_->layer()) {
    background_layer_.SetBounds(host_layer->bounds());
  }
}

void BlurredBackgroundShield::OnViewThemeChanged(views::View* observed_view) {
  UpdateBackgroundColor();
}

void BlurredBackgroundShield::StackLayerBelowHost() {
  // If the background layer is added to the host region below, we don't have to
  // manually restack it.
  CHECK(!add_layer_to_region_);

  // Otherwise, we should manually add the layer as a child layer of the host
  // view's parent layer. In case the parent layer owns other layers, we should
  // set the background layer below the host view layer.
  auto* host_layer = host_->layer();
  CHECK(host_layer);
  auto* host_parent_layer = host_->layer()->parent();
  CHECK(host_parent_layer);
  host_parent_layer->Add(&background_layer_);
  host_parent_layer->StackBelow(&background_layer_, host_layer);
  background_layer_.SetBounds(host_layer->bounds());
}

void BlurredBackgroundShield::UpdateBackgroundColor() {
  auto* color_provider = host_->GetColorProvider();
  const SkColor background_color =
      absl::holds_alternative<SkColor>(color_)
          ? absl::get<SkColor>(color_)
          : (color_provider
                 ? color_provider->GetColor(absl::get<ui::ColorId>(color_))
                 : gfx::kPlaceholderColor);
  // Only enable the background blur if the color is translucent.
  background_layer_.SetColor(background_color);
  if (SkColorGetA(background_color) != SK_AlphaOPAQUE && blur_sigma_) {
    background_layer_.SetBackgroundBlur(blur_sigma_);
    background_layer_.SetBackdropFilterQuality(
        ColorProvider::kBackgroundBlurQuality);
  } else {
    background_layer_.SetBackgroundBlur(0.0f);
  }
}

}  // namespace ash