chromium/ash/wm/resize_shadow_controller.cc

// Copyright 2012 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/wm/resize_shadow_controller.h"

#include <memory>

#include "ash/public/cpp/window_properties.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/resize_shadow.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/ui/base/window_state_type.h"
#include "chromeos/ui/frame/frame_utils.h"
#include "ui/aura/client/aura_constants.h"

namespace ash {

namespace {

// Lock shadow params
constexpr ResizeShadow::InitParams kLockParams{
    .thickness = 6,
    .shadow_corner_radius = 6,
    .window_corner_radius = 2,
    .opacity = 0.3f,
    .color = gfx::kGoogleGrey900,
    .hit_test_enabled = false,
    .hide_duration_ms = 0,
};

}  // namespace

ResizeShadowController::ResizeShadowController() = default;

ResizeShadowController::~ResizeShadowController() {
  RemoveAllShadows();
}

void ResizeShadowController::ShowShadow(aura::Window* window, int hit_test) {
  RecreateShadowIfNeeded(window);
  if (ShouldShowShadowForWindow(window) && window->IsVisible())
    GetShadowForWindow(window)->ShowForHitTest(hit_test);
}

void ResizeShadowController::TryShowAllShadows() {
  for (const auto& shadow : window_shadows_)
    UpdateShadowVisibility(shadow.first, shadow.first->IsVisible());
}

void ResizeShadowController::HideShadow(aura::Window* window) {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (!shadow)
    return;
  UpdateShadowVisibility(window, false);
}

void ResizeShadowController::HideAllShadows() {
  for (auto& shadow : window_shadows_) {
    if (!shadow.second)
      continue;

    switch (shadow.second->type_) {
      case ResizeShadowType::kLock: {  // Hides lock style of shadow
        UpdateShadowVisibility(shadow.first, false);
        break;
      }
      case ResizeShadowType::kUnlock: {  // Deletes unlock style of shadow
        shadow.second.reset();
        break;
      }
    }
  }
}

void ResizeShadowController::OnCrossFadeAnimationCompleted(
    aura::Window* window) {
  if (auto* shadow = GetShadowForWindow(window)) {
    shadow->ReparentLayer();
  }
}

void ResizeShadowController::RemoveAllShadows() {
  windows_observation_.RemoveAllObservations();
  window_shadows_.clear();
}

void ResizeShadowController::OnWindowHierarchyChanged(
    const aura::WindowObserver::HierarchyChangeParams& params) {
  ResizeShadow* shadow = GetShadowForWindow(params.target);
  if (shadow)
    shadow->ReparentLayer();
}

void ResizeShadowController::OnWindowVisibilityChanging(aura::Window* window,
                                                        bool visible) {
  UpdateShadowVisibility(window, visible);
}

void ResizeShadowController::OnWindowBoundsChanged(
    aura::Window* window,
    const gfx::Rect& old_bounds,
    const gfx::Rect& new_bounds,
    ui::PropertyChangeReason reason) {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (shadow && window->GetProperty(aura::client::kUseWindowBoundsForShadow))
    shadow->UpdateBoundsAndVisibility();
}

void ResizeShadowController::OnWindowStackingChanged(aura::Window* window) {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (shadow)
    shadow->ReparentLayer();
}

void ResizeShadowController::OnWindowDestroying(aura::Window* window) {
  windows_observation_.RemoveObservation(window);
  window_shadows_.erase(window);
}

void ResizeShadowController::OnWindowPropertyChanged(aura::Window* window,
                                                     const void* key,
                                                     intptr_t old) {
  if (key == aura::client::kShowStateKey) {
    UpdateShadowVisibility(window, window->IsVisible());
    return;
  }

  // If the resize shadow is being shown, ensure that shadow is configured
  // correctly for either a rounded window or squared window.
  if (ShouldShowShadowForWindow(window) &&
      key == aura::client::kWindowCornerRadiusKey) {
    RecreateShadowIfNeeded(window);
    UpdateShadowVisibility(window, window->IsVisible());
    return;
  }
}

void ResizeShadowController::OnWindowAddedToRootWindow(aura::Window* window) {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (shadow) {
    shadow->OnWindowParentToRootWindow();
  }
}

void ResizeShadowController::UpdateResizeShadowBoundsOfWindow(
    aura::Window* window,
    const gfx::Rect& bounds) {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (shadow)
    shadow->UpdateBounds(bounds);
}

ResizeShadow* ResizeShadowController::GetShadowForWindowForTest(
    aura::Window* window) {
  return GetShadowForWindow(window);
}

void ResizeShadowController::RecreateShadowIfNeeded(aura::Window* window) {
  if (!windows_observation_.IsObservingSource(window))
    windows_observation_.AddObservation(window);
  ResizeShadow* shadow = GetShadowForWindow(window);
  const ash::ResizeShadowType type =
      window->GetProperty(ash::kResizeShadowTypeKey);
  const int window_corner_radius =
      window->GetProperty(aura::client::kWindowCornerRadiusKey);
  const bool has_rounded_window =
      chromeos::features::IsRoundedWindowsEnabled() && window_corner_radius > 0;

  // If the `window` has a resize shadow with the requested type and the shadow
  // is configured for a rounded window, no need to recreate it.
  if (shadow && shadow->type_ == type &&
      shadow->is_for_rounded_window() == has_rounded_window) {
    return;
  }

  ResizeShadow::InitParams params;
  if (type == ResizeShadowType::kLock) {
    params = kLockParams;
  }

  // Configure window and shadow corner radius when `window` has rounded
  // corners.
  if (has_rounded_window) {
    params.thickness = 6;
    params.window_corner_radius = window_corner_radius;
    params.shadow_corner_radius = 16;
    params.is_for_rounded_window = true;
  }

  auto new_shadow = std::make_unique<ResizeShadow>(window, params, type);

  auto it = window_shadows_.find(window);
  if (it == window_shadows_.end())
    window_shadows_.insert(std::make_pair(window, std::move(new_shadow)));
  else
    it->second = std::move(new_shadow);
}

ResizeShadow* ResizeShadowController::GetShadowForWindow(
    aura::Window* window) const {
  auto it = window_shadows_.find(window);
  return it != window_shadows_.end() ? it->second.get() : nullptr;
}

void ResizeShadowController::UpdateShadowVisibility(aura::Window* window,
                                                    bool visible) const {
  ResizeShadow* shadow = GetShadowForWindow(window);
  if (!shadow)
    return;

  if (shadow->type_ == ResizeShadowType::kLock) {
    visible &= ShouldShowShadowForWindow(window);
    if (visible)
      shadow->ShowForHitTest();
  }

  if (!visible)
    shadow->Hide();
}

bool ResizeShadowController::ShouldShowShadowForWindow(
    aura::Window* window) const {
  // Hide the shadow if it's a maximized/fullscreen/minimized window or the
  // overview mode is active.
  ui::WindowShowState show_state =
      window->GetProperty(aura::client::kShowStateKey);
  return show_state != ui::SHOW_STATE_FULLSCREEN &&
         show_state != ui::SHOW_STATE_MAXIMIZED &&
         show_state != ui::SHOW_STATE_MINIMIZED &&
         !Shell::Get()->overview_controller()->InOverviewSession();
}

}  // namespace ash