1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131

ash / style / blurred_background_shield.cc [blame]

// 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