910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/widget/sublevel_manager.h"

#include <algorithm>

#include "build/build_config.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h"

namespace {

bool ShouldStackAboveParent(views::Widget* widget) {
#if !BUILDFLAG(IS_MAC)
  return false;
#else
  // macOS bug: a child widget might be rendered behind its parent in fullscreen
  // if the child is not explicitly StackAbove()'ed its parent.
  int level = 0;
  views::Widget* root = widget;
  while (root->parent()) {
    root = root->parent();
    // StackAbove() will make `widget` visible. We don't want this when its
    // ancestor is invisible.
    if (!root->IsVisible()) {
      return false;
    }
    level++;
  }
  // Only StackAbove() when `widget` is a grandchild (or deeper) of the root
  // fullscreen window.
  return level > 1 && root->IsFullscreen();
#endif
}

}  // namespace

namespace views {

SublevelManager::SublevelManager(Widget* owner, int sublevel)
    : owner_(owner), sublevel_(sublevel) {
  owner_observation_.Observe(owner);
}

SublevelManager::~SublevelManager() = default;

void SublevelManager::SetSublevel(int sublevel) {
  sublevel_ = sublevel;
  EnsureOwnerSublevel();
}

int SublevelManager::GetSublevel() const {
  return sublevel_;
}

void SublevelManager::EnsureOwnerSublevel() {
  // Walk through the path to the root and ensure sublevel on every widget
  // on the path. This is to work around the behavior on some platforms
  // where showing an activatable widget brings its ancestors to the front.
  Widget* parent = owner_->parent();
  Widget* child = owner_;
  while (parent && parent->GetSublevelManager()->IsTrackingChildWidget(child)) {
    parent->GetSublevelManager()->OrderChildWidget(child);
    child = parent;
    parent = parent->parent();
  }
}

void SublevelManager::EnsureOwnerTreeSublevel() {
  for (Widget* child : children_) {
    child->GetSublevelManager()->EnsureOwnerTreeSublevel();
  }

  if (Widget* parent = owner_->parent()) {
    parent->GetSublevelManager()->OrderChildWidget(owner_);
  }
}

void SublevelManager::OnWidgetChildAdded(Widget* owner, Widget* child) {
  CHECK_EQ(owner, owner_);
  CHECK(!base::Contains(children_, child));
  CHECK_EQ(child->parent(), owner_);
  children_.push_back(child);
}

void SublevelManager::OnWidgetChildRemoved(Widget* owner, Widget* child) {
  CHECK_EQ(owner, owner_);
  // During shutdown a child might get untracked more than once by the same
  // parent. We don't want to DCHECK on that.
  std::erase(children_, child);
}

void SublevelManager::OrderChildWidget(Widget* child) {
  auto removed = std::ranges::remove(children_, child);
  DCHECK_EQ(1u, removed.size());
  children_.erase(removed.begin(), removed.end());

  if (ShouldStackAboveParent(child)) {
    child->StackAboveWidget(owner_);
  }

  ui::ZOrderLevel child_level = child->GetZOrderLevel();
  auto insert_it = FindInsertPosition(child);

  // Stacking above an invisible widget is a no-op on Mac. Therefore, find only
  // visible ones.
  auto find_visible_widget_of_same_level = [child_level](Widget* widget) {
    return widget->IsVisible() && widget->GetZOrderLevel() == child_level;
  };

  auto prev_it = std::ranges::find_if(std::make_reverse_iterator(insert_it),
                                      std::crend(children_),
                                      find_visible_widget_of_same_level);

  if (prev_it == children_.rend()) {
    // x11 bug: stacking above the base `owner_` will cause `child` to become
    // unresponsive after the base widget is minimized. As a workaround, we
    // position `child` relative to the next child widget.

    // Find the closest next widget at the same level.
    auto next_it = std::ranges::find_if(insert_it, std::cend(children_),
                                        find_visible_widget_of_same_level);

    // Put `child` below `next_it`.
    if (next_it != std::end(children_)) {
      child->StackAboveWidget(*next_it);
      (*next_it)->StackAboveWidget(child);
    }
  } else {
    child->StackAboveWidget(*prev_it);
  }

  children_.insert(insert_it, child);
}

bool SublevelManager::IsTrackingChildWidget(Widget* child) {
  return std::ranges::find(children_, child) != children_.end();
}

SublevelManager::ChildIterator SublevelManager::FindInsertPosition(
    Widget* child) const {
  ui::ZOrderLevel child_level = child->GetZOrderLevel();
  int child_sublevel = child->GetZOrderSublevel();
  return std::ranges::find_if(children_, [&](Widget* widget) {
    return widget->GetZOrderLevel() == child_level &&
           widget->GetZOrderSublevel() > child_sublevel;
  });
}

}  // namespace views