chromium/ash/user_education/welcome_tour/welcome_tour_window_minimizer.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/user_education/welcome_tour/welcome_tour_window_minimizer.h"

#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/window_state.h"
#include "base/check.h"
#include "base/task/sequenced_task_runner.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tracker.h"
#include "ui/wm/core/scoped_animation_disabler.h"

namespace ash {
namespace {

// Helpers ---------------------------------------------------------------------

// Returns whether a window's children should be minimized while
// `WelcomeTourWindowMinimizer` exists.
bool ShouldMinimizeChildren(aura::Window* window) {
  return window && (desks_util::IsDeskContainer(window) ||
                    window->GetId() == kShellWindowId_FloatContainer);
}

// Minimizes a window, if it is possible to do so.
void Minimize(aura::Window* window) {
  auto* state = WindowState::Get(window);
  if (state && !state->IsMinimized()) {
    state->Minimize();
  }
}

// Minimizes all windows in a `aura::WindowTracker`, if they are in containers
// that should have their windows minimized. Can be called asynchronously.
void MaybeMinimize(aura::WindowTracker* window_tracker) {
  for (aura::Window* window : window_tracker->windows()) {
    if (ShouldMinimizeChildren(window->parent())) {
      wm::ScopedAnimationDisabler animation_disabler(window);
      Minimize(window);
    }
  }
}

}  // namespace

// WelcomeTourWindowMinimizer --------------------------------------------------

WelcomeTourWindowMinimizer::WelcomeTourWindowMinimizer() {
  for (aura::Window* root_window : Shell::GetAllRootWindows()) {
    OnRootWindowAdded(root_window);
  }

  shell_observation_.Observe(Shell::Get());
}

WelcomeTourWindowMinimizer::~WelcomeTourWindowMinimizer() = default;

void WelcomeTourWindowMinimizer::OnWindowDestroying(aura::Window* window) {
  root_window_observations_.RemoveObservation(window);
}

void WelcomeTourWindowMinimizer::OnWindowHierarchyChanged(
    const HierarchyChangeParams& params) {
  // If the relevant window is entering a container that should be minimized,
  // begin keeping it minimized.
  if (ShouldMinimizeChildren(params.new_parent)) {
    app_window_minimizer_.AddWindow(params.target);
  } else if (ShouldMinimizeChildren(params.old_parent)) {
    // If the window is leaving a should-minimize container and not entering
    // another one, stop keeping it minimized.
    app_window_minimizer_.RemoveWindow(params.target);
  }
}

void WelcomeTourWindowMinimizer::OnRootWindowAdded(aura::Window* root_window) {
  root_window_observations_.AddObservation(root_window);

  std::vector<aura::Window*> containers =
      desks_util::GetDesksContainers(root_window);

  auto* float_container =
      root_window->GetChildById(kShellWindowId_FloatContainer);
  CHECK(float_container);
  containers.push_back(float_container);

  for (auto* container : containers) {
    if (ShouldMinimizeChildren(container)) {
      for (aura::Window* child : container->children()) {
        app_window_minimizer_.AddWindow(child);
      }
    }
  }
}

// WelcomeTourMinimized::AppWindowMinimizer ------------------------------------

WelcomeTourWindowMinimizer::AppWindowMinimizer::AppWindowMinimizer() = default;

WelcomeTourWindowMinimizer::AppWindowMinimizer::~AppWindowMinimizer() = default;

void WelcomeTourWindowMinimizer::AppWindowMinimizer::AddWindow(
    aura::Window* window) {
  Minimize(window);
  if (!app_window_observations_.IsObservingSource(window)) {
    app_window_observations_.AddObservation(window);
  }
}

void WelcomeTourWindowMinimizer::AppWindowMinimizer::RemoveWindow(
    aura::Window* window) {
  if (app_window_observations_.IsObservingSource(window)) {
    app_window_observations_.RemoveObservation(window);
  }
}

void WelcomeTourWindowMinimizer::AppWindowMinimizer::OnWindowDestroying(
    aura::Window* window) {
  app_window_observations_.RemoveObservation(window);
}

void WelcomeTourWindowMinimizer::AppWindowMinimizer::OnWindowVisibilityChanged(
    aura::Window* window,
    bool visible) {
  if (visible) {
    // Minimize the window asynchronously to avoid changing visibility directly
    // from within a visibility event.
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&MaybeMinimize,
                       base::Owned(std::make_unique<aura::WindowTracker>(
                           aura::WindowTracker::WindowList{window}))));
  }
}

}  // namespace ash