blob: f4fcb76524b0696483f00cd2545edb7617fdb481 [file]
// Copyright 2026 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/test/mock_activation_controller.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "ui/views/buildflags.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_activation_delegate.h"
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#elif BUILDFLAG(IS_MAC)
#include "ui/views/widget/native_widget_mac.h"
#endif
namespace views::test {
namespace {
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
DesktopNativeWidgetAura* GetNativeWidget(Widget* widget) {
CHECK(widget->GetIsDesktopWidget());
auto* native_widget_private = widget->native_widget_private();
return static_cast<DesktopNativeWidgetAura*>(native_widget_private);
}
#elif BUILDFLAG(IS_MAC)
NativeWidgetMac* GetNativeWidget(Widget* widget) {
auto* native_widget_private = widget->native_widget_private();
return static_cast<NativeWidgetMac*>(native_widget_private);
}
#endif
void SetActivationState(Widget* widget, bool active) {
CHECK(widget);
auto* native_widget = GetNativeWidget(widget);
CHECK(native_widget);
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
native_widget->HandleActivationChanged(active);
#else
native_widget->OnWindowKeyStatusChanged(active, active);
#endif
}
} // namespace
MockActivationController::MockActivationController() = default;
MockActivationController::~MockActivationController() {
for (auto widget : widgets_) {
widget->RemoveObserver(this);
}
}
void MockActivationController::MaybeActivate(Widget* widget, bool activate) {
CHECK(widget);
CHECK(widget->is_top_level());
auto current_active = active_widget_;
// Do not change the activation even if activate == false.
if (current_active == widget) {
return;
}
auto iter = std::ranges::find(widgets_, widget);
if (iter == widgets_.end()) {
widget->AddObserver(this);
widgets_.push_back(widget);
} else if (activate) {
// Move the widget to the top.
widgets_.erase(iter);
widgets_.push_back(widget);
}
if (!activate) {
// no need to update active_widget_;
return;
}
CHECK(widget->CanActivate());
active_widget_ = widget;
if (current_active) {
SetActivationState(current_active, false);
}
SetActivationState(widget, true);
}
void MockActivationController::Deactivate(Widget* widget) {
CHECK(widget);
CHECK(widget->is_top_level());
if (active_widget_ == widget) {
Widget* next_active = nullptr;
if (widgets_.size() > 1) {
auto iter = std::ranges::find(widgets_, widget);
CHECK(iter != widgets_.end());
widgets_.erase(iter);
auto riter = FindActivatableWidget();
if (riter != widgets_.rend()) {
next_active = (*riter).get();
// reverse iterator
riter++;
widgets_.insert(riter.base(), widget);
}
}
active_widget_ = next_active;
SetActivationState(widget, false);
if (next_active) {
SetActivationState(next_active, true);
}
}
}
bool MockActivationController::IsActive(const Widget* widget) {
return widget == active_widget_;
}
void MockActivationController::OnWidgetDestroying(Widget* widget) {
widget->RemoveObserver(this);
auto iter = std::ranges::find(widgets_, widget);
CHECK(iter != widgets_.end());
widgets_.erase(iter);
if (active_widget_ == widget) {
active_widget_ = nullptr;
if (!widgets_.empty()) {
auto riter = FindActivatableWidget();
if (riter != widgets_.rend()) {
active_widget_ = (*riter).get();
}
}
if (active_widget_) {
SetActivationState(active_widget_, true);
}
}
}
void MockActivationController::OnWidgetVisibilityChanged(Widget* widget,
bool visible) {
auto iter = std::ranges::find(widgets_, widget);
if (iter == widgets_.end()) {
widgets_.push_back(widget);
}
if (!visible && IsActive(widget)) {
Deactivate(widget);
}
}
MockActivationController::WidgetList::reverse_iterator
MockActivationController::FindActivatableWidget() {
for (auto iter = widgets_.rbegin(); iter != widgets_.rend(); iter++) {
if ((*iter)->IsVisible() && (*iter)->CanActivate()) {
return iter;
}
}
return widgets_.rend();
}
} // namespace views::test